Skip to content

Commit

Permalink
Merge pull request #3 from saguiras/main
Browse files Browse the repository at this point in the history
merge main to cirq
  • Loading branch information
saguiras authored Mar 7, 2024
2 parents 5e07c3b + bc41434 commit 2d1c399
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Test
run: echo ok
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Dependencies
Expand Down
21 changes: 21 additions & 0 deletions example/scripts/google_execution_trials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from mpqp.gates import H, Rx, Ry, Rz
from mpqp import QCircuit
from mpqp.measures import BasisMeasure
from mpqp.execution.devices import GOOGLEDevice, IBMDevice
from mpqp.execution import run

circuit = QCircuit(3)
circuit.add(H(0))
circuit.add(H(1))
circuit.add(H(2))
circuit.add(Rx(1.76, 1))
circuit.add(Ry(1.76, 1))
circuit.add(Rz(1.987, 0))
circuit.add(BasisMeasure([0, 1, 2], shots=1000))

print(circuit)

results = run(circuit, [GOOGLEDevice.CIRQ, GOOGLEDevice.PROCESSOR_RAINBOW, GOOGLEDevice.PROCESSOR_WEBER])
print(results)

#FIXME demonstation off circuit_to_grid() for processor
1 change: 1 addition & 0 deletions mpqp/core/languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ class Language(Enum):
QISKIT = 0
MY_QLM = 1
BRAKET = 2
CIRQ = 3
19 changes: 19 additions & 0 deletions mpqp/execution/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,22 @@ def from_arn(arn: str):
if elem.value in arn:
return elem
return None

class GOOGLEDevice(AvailableDevice):
"""Enum regrouping all available devices provided by CIRQ."""

CIRQ = "LocalSimulator"
PROCESSOR_RAINBOW = "rainbow"
PROCESSOR_WEBER = "weber"

def is_remote(self):
return False

def is_gate_based(self) -> bool:
return True

def is_simulator(self) -> bool:
return True

def is_processor(self) -> bool:
return self.name.startswith("PROCESSOR")
157 changes: 157 additions & 0 deletions mpqp/execution/providers_execution/google_execution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
from typing import Optional

from typeguard import typechecked

from mpqp.execution.devices import GOOGLEDevice
from ..job import Job, JobType
from ..result import Result, Sample, StateVector
from mpqp.qasm import qasm2_to_cirq_Circuit

from mpqp.core.instruction.measurement import ComputationalBasis
from mpqp.core.instruction.measurement.basis_measure import BasisMeasure
from mpqp.core.instruction.measurement.expectation_value import (
ExpectationMeasure,
Observable,
)

from cirq import Simulator, RouteCQC, optimize_for_target_gateset, state_vector_to_probabilities, circuits
from cirq import Result as cirq_result
from cirq_google import engine, noise_properties_from_calibration, NoiseModelFromGoogleNoiseProperties, SycamoreTargetGateset
from qsimcirq import QSimSimulator


@typechecked
def run_google(job: Job) -> Result:
"""
Execute the job on the right Google device precised in the job in parameter.
This function is not meant to be used directly, please use ``runner.run(...)`` instead.
Args:
job: Job to be executed.
Returns:
A Result after submission and execution of the job.
"""
return run_local(job) if not job.device.is_remote() else print("none")


@typechecked
def run_local(job: Job) -> Result:

cirq_circuit = qasm2_to_cirq_Circuit(job.circuit.to_qasm2())
sim = Simulator()
if job.device.is_processor():
if job.job_type != JobType.SAMPLE:
raise NotImplementedError(
f"Does not handle {job.job_type} for processor for the moment"
)
cirq_circuit, sim = circuit_to_processor_cirq_Circuit(job, cirq_circuit)


if job.job_type == JobType.STATE_VECTOR:
result_sim = sim.simulate(cirq_circuit)
result = extract_result_STATE_VECTOR(result_sim, job, GOOGLEDevice.CIRQ)
elif job.job_type == JobType.SAMPLE:
assert isinstance(job.measure, BasisMeasure)
if isinstance(job.measure.basis, ComputationalBasis):
if job.device.is_processor():
result_sim = sim.get_sampler(job.device.value).run(cirq_circuit, repetitions=job.measure.shots)
else :
result_sim = sim.run(cirq_circuit, repetitions=job.measure.shots)
else:
raise NotImplementedError(
"Does not handle other basis than the ComputationalBasis for the moment"
)

result = extract_result_SAMPLE(result_sim, job, GOOGLEDevice.CIRQ)
elif job.job_type == JobType.OBSERVABLE:
assert isinstance(job.measure, ExpectationMeasure)
raise NotImplementedError(
"Does not handle OBSERVABLE for the moment"
)
#result = sim.simulate_expectation_values(cirq_circuit, job.measure.observable)
else:
raise ValueError(f"Job type {job.job_type} not handled")


return result

@typechecked
def circuit_to_processor_cirq_Circuit(job: Job, cirq_circuit: circuits):

processor_id = job.device.value
cal = engine.load_median_device_calibration(processor_id)
noise_props = noise_properties_from_calibration(cal)
noise_model = NoiseModelFromGoogleNoiseProperties(noise_props)
sim = QSimSimulator(noise=noise_model)

device = engine.create_device_from_processor_id(processor_id)

router = RouteCQC(device.metadata.nx_graph)

rcirc, initial_map, swap_map = router.route_circuit(cirq_circuit)

fcirc = optimize_for_target_gateset(rcirc, gateset = SycamoreTargetGateset())

device.validate_circuit(fcirc)

sim_processor = engine.SimulatedLocalProcessor(
processor_id=processor_id, sampler=sim, device=device, calibrations={cal.timestamp // 1000: cal}
)
sim_engine = engine.SimulatedLocalEngine([sim_processor])

return fcirc, sim_engine


def extract_result_SAMPLE(
result: cirq_result,
job: Optional[Job] = None,
device: Optional[GOOGLEDevice] = None,
) -> Result:
"""
Parse a result from Cirq execution into an mpqp Result.
Args:
result: Result returned by Cirq after run of the circuit.
job: Original mpqp circuit used to generate the run. Used to retrieve more easily info to instantiate the result.
device: Cirq Device on which the circuit was simulated.
repetitions: Number of repetitions for the circuit execution.
Returns:
A Result containing the result info extracted from the Cirq result.
"""
nb_qubits = job.circuit.nb_qubits

counts = result.multi_measurement_histogram(keys=result.records.keys())

data = [
Sample(
bin_str=''.join(map(str, state)), probability=count/sum(counts.values()), nb_qubits=nb_qubits
)
for (state, count) in counts.items()
]
return Result(job, data, None, job.measure.shots)




def extract_result_STATE_VECTOR(
result: cirq_result,
job: Optional[Job] = None,
device: Optional[GOOGLEDevice] = None,
) -> Result:
state_vector = result.final_state_vector
state_vector = StateVector(state_vector, job.circuit.nb_qubits, state_vector_to_probabilities(state_vector))
return Result(job, state_vector, 0, 0)


def extract_result_OBSERVABLE(
result: cirq_result,
job: Optional[Job] = None,
device: Optional[GOOGLEDevice] = None,
) -> Result:
shots = 0 if len(result.metadata[0]) == 0 else result.metadata[0]["shots"]
variance = (
None if len(result.metadata[0]) == 0 else result.metadata[0]["variance"]
)
return Result(job, result.values[0], variance, shots)
34 changes: 18 additions & 16 deletions mpqp/execution/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ def amplitudes(self):
return self.vector

def __str__(self):
return f"""
State vector: {self.vector}
Probabilities: {self.probabilities}
Number of qubits: {self.nb_qubits}"""
return f"State vector: {self.vector}\nProbabilities: {self.probabilities}\nNumber of qubits: {self.nb_qubits}"


@typechecked
Expand Down Expand Up @@ -290,6 +287,7 @@ def __init__(
" either `count` or `probability` (and the non-None "
"attribute amongst the two must be the same in all samples)."
)
self.samples.sort(key=lambda sample: sample.bin_str)
else:
raise ValueError(f"{job.job_type} not handled")

Expand Down Expand Up @@ -363,24 +361,28 @@ def counts(self) -> list[int]:

def __str__(self):
header = f"Result: {type(self.device).__name__}, {self.device.name}"

if self.job.job_type == JobType.SAMPLE:
samples_str = ("\n" + " " * 16).join(map(str, self.samples))
samples_str = "\n".join(map(lambda s: f"{' ' * 1}{s}", map(str, self.samples)))
cleaned_probas = str(self._probabilities).replace("\n", " ")
return header + dedent(
f"""
Counts: {self._counts}
Probabilities: {cleaned_probas}
{samples_str}
Error: {self.error}\n\n"""
return (
f"{header}\n"
f"Counts: {self._counts}\n"
f"Probabilities: {cleaned_probas}\n"
f"{samples_str}\n"
f"Error: {self.error}\n\n"
)

if self.job.job_type == JobType.STATE_VECTOR:
return f"""{header}\n{self._state_vector}\n\n"""
return f"{header}\n{self._state_vector}\n"

if self.job.job_type == JobType.OBSERVABLE:
return header + dedent(
f"""
Expectation value: {self.expectation_value}
Error/Variance: {self.error}\n\n"""
return (
f"{header}\n"
f"Expectation value: {self.expectation_value}\n"
f"Error/Variance: {self.error}\n"
)

raise NotImplementedError(
f"Job type {self.job.job_type} not implemented for __str__ method"
)
Expand Down
5 changes: 4 additions & 1 deletion mpqp/execution/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
ExpectationMeasure,
Observable,
)
from mpqp.execution.devices import AvailableDevice, IBMDevice, ATOSDevice, AWSDevice
from mpqp.execution.devices import AvailableDevice, IBMDevice, ATOSDevice, AWSDevice, GOOGLEDevice
from mpqp.execution.providers_execution.aws_execution import (
run_braket,
submit_job_braket,
)
from mpqp.execution.providers_execution.atos_execution import run_atos, submit_QLM
from mpqp.execution.providers_execution.ibm_execution import run_ibm, submit_ibmq
from mpqp.execution.providers_execution.google_execution import run_google
from mpqp.execution.result import Result, BatchResult
from mpqp.execution.job import Job, JobType, JobStatus
from mpqp.tools.errors import RemoteExecutionError
Expand Down Expand Up @@ -143,6 +144,8 @@ def _run_single(
return run_atos(job)
elif isinstance(device, AWSDevice):
return run_braket(job)
elif isinstance(device, GOOGLEDevice):
return run_google(job)
else:
raise NotImplementedError(f"Device {device} not handled")

Expand Down
1 change: 1 addition & 0 deletions mpqp/qasm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .qasm_to_braket import qasm3_to_braket_Program
from .qasm_to_myqlm import qasm2_to_myqlm_Circuit
from .qasm_to_qiskit import qasm2_to_QuantumCircuit
from .qasm_to_cirq import qasm2_to_cirq_Circuit
from .open_qasm_2_and_3 import (
open_qasm_2_to_3,
open_qasm_file_conversion_2_to_3,
Expand Down
19 changes: 19 additions & 0 deletions mpqp/qasm/qasm_to_cirq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""File regrouping all features for translating QASM code to cirq objects """
from cirq import Circuit
from cirq.contrib.qasm_import import circuit_from_qasm
from typeguard import typechecked



@typechecked
def qasm2_to_cirq_Circuit(qasm_str: str) -> Circuit:
"""
Converting a OpenQASM 2.0 code into a cirq Circuit
Args:
qasm_str: a string representing the OpenQASM 2.0 code
Returns:
a Circuit equivalent to the QASM code in parameter
"""
return circuit_from_qasm(qasm_str)
5 changes: 4 additions & 1 deletion tests/example/test_demonstrations.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from mpqp import QCircuit
from mpqp.core.instruction.measurement import Observable, ExpectationMeasure
from mpqp.execution.devices import AWSDevice, ATOSDevice, IBMDevice
from mpqp.execution.devices import AWSDevice, ATOSDevice, IBMDevice, GOOGLEDevice
from mpqp.gates import *
from mpqp.measures import BasisMeasure
from mpqp.execution import run
Expand Down Expand Up @@ -41,6 +41,7 @@ def test_sample_demo():
ATOSDevice.MYQLM_PYLINALG,
ATOSDevice.MYQLM_CLINALG,
AWSDevice.BRAKET_LOCAL_SIMULATOR,
GOOGLEDevice.CIRQ
],
)

Expand Down Expand Up @@ -75,6 +76,7 @@ def test_statevector_demo():
ATOSDevice.MYQLM_PYLINALG,
ATOSDevice.MYQLM_CLINALG,
AWSDevice.BRAKET_LOCAL_SIMULATOR,
GOOGLEDevice.CIRQ
],
)

Expand All @@ -89,6 +91,7 @@ def test_statevector_demo():
ATOSDevice.MYQLM_PYLINALG,
ATOSDevice.MYQLM_CLINALG,
AWSDevice.BRAKET_LOCAL_SIMULATOR,
GOOGLEDevice.CIRQ
],
)

Expand Down
Loading

0 comments on commit 2d1c399

Please sign in to comment.