Skip to content

Commit 0adf81f

Browse files
Cirq documentation + format
1 parent d960eb8 commit 0adf81f

File tree

9 files changed

+147
-115
lines changed

9 files changed

+147
-115
lines changed

docs/execution-extras.rst

+16-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ for you are most likely:
1010
- IBM's :func:`get_all_job_ids<mpqp.execution.connection.ibm_connection.get_all_job_ids>`;
1111
- Eviden's :func:`get_all_job_ids<mpqp.execution.connection.qlm_connection.get_all_job_ids>`;
1212
- AWS's :func:`get_all_task_ids<mpqp.execution.connection.aws_connection.get_all_task_ids>`;
13+
- Google's :func:`get_all_task_ids<mpqp.execution.connection.google_connection.get_all_task_ids>`;
1314
- The :ref:`con-setup` section.
1415

1516
Provider specifics
@@ -49,16 +50,29 @@ __________
4950
AWS
5051
^^^
5152

53+
Connection
54+
__________
55+
56+
.. automodule:: mpqp.execution.connection.aws_connection
57+
58+
Execution
59+
__________
60+
5261
.. automodule:: mpqp.execution.providers.aws
5362

63+
Google
64+
^^^^^^
65+
5466
Connection
5567
__________
5668

57-
.. automodule:: mpqp.execution.connection.aws_connection
69+
.. automodule:: mpqp.execution.connection.google_connection
5870

5971
Execution
6072
__________
6173

74+
.. automodule:: mpqp.execution.providers.google
75+
6276
.. _con-setup:
6377

6478
Connection setup script
@@ -70,3 +84,4 @@ On disk configuration manager
7084
-----------------------------
7185

7286
.. automodule:: mpqp.execution.connection.env_manager
87+

docs/qasm.rst

+7-3
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,17 @@ OpenQASM 2.0 code. This feature enables users to seamlessly transition
130130
their quantum circuits from OpenQASM to Cirq, leveraging Cirq's capabilities
131131
and features.
132132

133-
At present, the Cirq parser lacks native support for certain OpenQASM 2.0
134-
operations such as ``cu1``, ``crz``, ``cu3``, ``reset``, and ``u0``.
133+
the Cirq parser lacks native support for certain OpenQASM 2.0 operations such as
134+
``cu1``, ``crz``, ``cu3``, ``reset``, ``u0``, ``p``, ``cp``, ``u``, ``rzz``,
135+
``rxx`` and custom ``gate``.
135136
To address this limitation, we've temporarily incorporated these gates directly
136-
into the parser. This ensures seamless generation of circuits from QASM code
137+
into the parser and for the custom gate we replace directly by native gate in
138+
the qasm str. This ensures seamless generation of circuits from QASM code
137139
using the qasm2_to_cirq_Circuit function.
138140

139141
:func:`qasm2_to_cirq_Circuit<mpqp.qasm.qasm_to_cirq.qasm2_to_cirq_Circuit>` to generate
140142
the circuit from the qasm code.
141143

142144
.. autofunction:: mpqp.qasm.qasm_to_cirq.qasm2_to_cirq_Circuit
145+
146+
:func:`replace_custom_gates<mpqp.qasm.remplace_custom_gates.replace_custom_gates>` t Replaces instances of custom gates with their definitions in QASM code.

examples/scripts/observable_paulistring.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
from mpqp import QCircuit
66
from mpqp.execution import run
77
from mpqp.measures import ExpectationMeasure, Observable
8-
from mpqp.execution.devices import ATOSDevice, IBMDevice, AWSDevice
8+
from mpqp.execution.devices import IBMDevice, AWSDevice, ATOSDevice, GOOGLEDevice
99

10-
from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z, PauliString
10+
from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z
1111

1212
obs = Observable(
1313
np.array(
@@ -23,8 +23,7 @@
2323

2424
ps_obs = Observable(1 * I @ Z + 1 * I @ I)
2525

26-
obs = ps_obs.matrix
27-
26+
print("obs == ps_obs:", obs.pauli_string == ps_obs.pauli_string)
2827
print(obs)
2928
print(ps_obs)
3029

@@ -38,5 +37,14 @@
3837
print(circuit)
3938

4039
# Running the computation on myQLM and on Aer simulator, then retrieving the results
41-
results = run(circuit, [AWSDevice.BRAKET_LOCAL_SIMULATOR])
40+
results = run(
41+
circuit,
42+
[
43+
ATOSDevice.MYQLM_PYLINALG,
44+
ATOSDevice.MYQLM_CLINALG,
45+
IBMDevice.AER_SIMULATOR,
46+
AWSDevice.BRAKET_LOCAL_SIMULATOR,
47+
GOOGLEDevice.CIRQ_LOCAL_SIMULATOR,
48+
],
49+
)
4250
print(results)

mpqp/execution/connection/google_connection.py

+24
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77

88

99
def test_ionq_connection() -> bool:
10+
"""
11+
Test the connection to the IonQ service.
12+
13+
Returns:
14+
bool: True if the connection is successful, False otherwise.
15+
"""
1016
service = ionq.Service(default_target="simulator")
1117
try:
1218
service.list_jobs()
@@ -17,6 +23,12 @@ def test_ionq_connection() -> bool:
1723

1824

1925
def config_ionq_account():
26+
"""
27+
Configure the IonQ account by setting the API token.
28+
29+
Returns:
30+
tuple: A message indicating the result of the configuration and an empty list.
31+
"""
2032
was_configured = get_env_variable("GOOGLE_IONQ_CONFIGURED") == "True"
2133

2234
if was_configured:
@@ -46,6 +58,18 @@ def config_ionq_account():
4658

4759

4860
def get_google_account_info() -> str:
61+
"""
62+
Get the IonQ API key from the environment variables.
63+
64+
Returns:
65+
str: A string containing the IonQ API key.
66+
"""
4967
ionq_api_key = get_env_variable("IONQ_API_KEY")
5068

5169
return f""" IONQ_api_key : '{ionq_api_key}'"""
70+
71+
def get_all_job_ids() -> list[str]:
72+
# TODO: This function should return a list of all the jobs ids
73+
74+
#list_jobs : https://quantumai.google/reference/python/cirq_google/engine/Engine#list_jobs
75+
return [""]

mpqp/execution/providers/google.py

+74-85
Original file line numberDiff line numberDiff line change
@@ -49,80 +49,16 @@ def run_google(job: Job) -> Result:
4949
Returns:
5050
A Result after submission and execution of the job.
5151
"""
52-
if type(job.device) != GOOGLEDevice:
53-
raise ValueError("Job device must be GOOGLEDevice")
54-
55-
if job.device.is_processor():
56-
return run_processor(job)
57-
elif job.device.is_remote():
58-
return run_google_remote(job)
59-
else:
60-
return run_local(job)
61-
62-
63-
@typechecked
64-
def run_processor(job: Job) -> Result:
65-
"""
66-
Executes the job locally on processor.
67-
68-
Args:
69-
job : The job to be executed.
70-
71-
Returns:
72-
Result: The result after submission and execution of the job..
73-
"""
74-
cal = load_median_device_calibration(job.device.value)
75-
device = create_device_from_processor_id(job.device.value)
76-
# noise_props = noise_properties_from_calibration(cal)
77-
# noise_model = NoiseModelFromGoogleNoiseProperties(noise_props)
78-
sim = QSimSimulator(noise=None)
79-
sim_processor = SimulatedLocalProcessor(
80-
processor_id=job.device.value,
81-
sampler=sim,
82-
device=device,
83-
calibrations={cal.timestamp // 1000: cal},
84-
)
85-
sim = SimulatedLocalEngine([sim_processor])
86-
87-
job_cirq_circuit = job.circuit.to_other_language(
88-
Language.CIRQ, processor_id=job.device.value
89-
)
90-
if not isinstance(job_cirq_circuit, Cirq_circuit):
91-
raise ValueError("Circuit must be Cirq_circuit")
92-
93-
if job.job_type == JobType.STATE_VECTOR:
94-
raise NotImplementedError(
95-
f"Does not handle {job.job_type} for processor for the moment"
96-
)
97-
elif job.job_type == JobType.OBSERVABLE:
98-
raise NotImplementedError(
99-
f"Does not handle {job.job_type} for processor for the moment"
100-
)
101-
elif job.job_type == JobType.SAMPLE:
102-
assert isinstance(job.measure, BasisMeasure)
103-
if isinstance(job.measure.basis, ComputationalBasis):
104-
result_sim = sim.get_sampler(job.device.value).run(
105-
job_cirq_circuit, repetitions=job.measure.shots
106-
)
107-
else:
108-
raise NotImplementedError(
109-
"Does not handle other basis than the ComputationalBasis for the moment"
110-
)
111-
else:
112-
raise ValueError(f"Job type {job.job_type} not handled")
113-
114-
return extract_result(result_sim, job, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR)
52+
return run_local(job) if not job.device.is_remote() else run_google_remote(job)
11553

11654

11755
@typechecked
11856
def run_google_remote(job: Job) -> Result:
11957

120-
if type(job.device) != GOOGLEDevice:
121-
raise ValueError("Job device must be GOOGLEDevice")
58+
assert type(job.device) == GOOGLEDevice
12259

12360
job_cirq_circuit = job.circuit.to_other_language(Language.CIRQ)
124-
if not isinstance(job_cirq_circuit, Cirq_circuit):
125-
raise ValueError("Circuit must be Cirq_circuit")
61+
assert isinstance(job_cirq_circuit, Cirq_circuit)
12662

12763
if job.device.is_ionq():
12864
if job.job_type != JobType.SAMPLE:
@@ -151,8 +87,7 @@ def run_google_remote(job: Job) -> Result:
15187
f" does not handle {job.device} for the moment only ionq is supported"
15288
)
15389

154-
result = extract_result(result_sim, job, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR)
155-
return result
90+
return extract_result(result_sim, job, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR)
15691

15792

15893
@typechecked
@@ -169,22 +104,22 @@ def run_local(job: Job) -> Result:
169104
Raises:
170105
ValueError: If the job device is not GOOGLEDevice.
171106
"""
172-
if type(job.device) != GOOGLEDevice:
173-
raise ValueError("Job device must be GOOGLEDevice")
107+
assert type(job.device) == GOOGLEDevice
174108

175-
job_cirq_circuit = job.circuit.to_other_language(Language.CIRQ)
109+
if job.device.is_processor():
110+
return run_local_processor(job)
176111

177-
if not isinstance(job_cirq_circuit, Cirq_circuit):
178-
raise ValueError("Circuit must be Cirq_circuit")
112+
job_cirq_circuit = job.circuit.to_other_language(Language.CIRQ)
113+
assert isinstance(job_cirq_circuit, Cirq_circuit)
179114

180-
sim = Simulator()
115+
simulator = Simulator()
181116

182117
if job.job_type == JobType.STATE_VECTOR:
183-
result_sim = sim.simulate(job_cirq_circuit)
118+
result_sim = simulator.simulate(job_cirq_circuit)
184119
elif job.job_type == JobType.SAMPLE:
185120
assert isinstance(job.measure, BasisMeasure)
186121
if isinstance(job.measure.basis, ComputationalBasis):
187-
result_sim = sim.run(job_cirq_circuit, repetitions=job.measure.shots)
122+
result_sim = simulator.run(job_cirq_circuit, repetitions=job.measure.shots)
188123
else:
189124
raise NotImplementedError(
190125
"Does not handle other basis than the ComputationalBasis for the moment"
@@ -195,23 +130,77 @@ def run_local(job: Job) -> Result:
195130
cirq_obs = job.measure.observable.to_other_language(
196131
language=Language.CIRQ, circuit=job_cirq_circuit
197132
)
198-
199-
if type(cirq_obs) != Cirq_PauliSum:
200-
raise ValueError("cirq_obs must be a Cirq_PauliSum object")
133+
assert type(cirq_obs) == Cirq_PauliSum
201134

202135
if job.measure.shots == 0:
203-
result_sim = sim.simulate_expectation_values(
136+
result_sim = simulator.simulate_expectation_values(
204137
job_cirq_circuit, observables=cirq_obs
205138
)
206139
else:
207140
result_sim = measure_observables(
208141
job_cirq_circuit,
209142
cirq_obs, # type: ignore[reportArgumentType]
210-
sim,
143+
simulator,
211144
stopping_criteria=RepetitionsStoppingCriteria(job.measure.shots),
212145
)
213146
else:
214147
raise ValueError(f"Job type {job.job_type} not handled")
148+
149+
return extract_result(result_sim, job, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR)
150+
151+
152+
@typechecked
153+
def run_local_processor(job: Job) -> Result:
154+
"""
155+
Executes the job locally on processor.
156+
157+
Args:
158+
job : The job to be executed.
159+
160+
Returns:
161+
Result: The result after submission and execution of the job.
162+
"""
163+
calibration = load_median_device_calibration(job.device.value)
164+
device = create_device_from_processor_id(job.device.value)
165+
166+
# noise_props = noise_properties_from_calibration(cal)
167+
# noise_model = NoiseModelFromGoogleNoiseProperties(noise_props)
168+
169+
simulator = QSimSimulator(noise=None)
170+
sim_processor = SimulatedLocalProcessor(
171+
processor_id=job.device.value,
172+
sampler=simulator,
173+
device=device,
174+
calibrations={calibration.timestamp // 1000: calibration},
175+
)
176+
simulator = SimulatedLocalEngine([sim_processor])
177+
178+
job_cirq_circuit = job.circuit.to_other_language(
179+
Language.CIRQ, processor_id=job.device.value
180+
)
181+
assert isinstance(job_cirq_circuit, Cirq_circuit)
182+
183+
if job.job_type == JobType.STATE_VECTOR:
184+
raise NotImplementedError(
185+
f"Does not handle {job.job_type} for processor for the moment"
186+
)
187+
elif job.job_type == JobType.OBSERVABLE:
188+
raise NotImplementedError(
189+
f"Does not handle {job.job_type} for processor for the moment"
190+
)
191+
elif job.job_type == JobType.SAMPLE:
192+
assert isinstance(job.measure, BasisMeasure)
193+
if isinstance(job.measure.basis, ComputationalBasis):
194+
result_sim = simulator.get_sampler(job.device.value).run(
195+
job_cirq_circuit, repetitions=job.measure.shots
196+
)
197+
else:
198+
raise NotImplementedError(
199+
"Does not handle other basis than the ComputationalBasis for the moment"
200+
)
201+
else:
202+
raise ValueError(f"Job type {job.job_type} not handled")
203+
215204
return extract_result(result_sim, job, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR)
216205

217206

@@ -246,19 +235,19 @@ def extract_result(
246235
if job.job_type == JobType.SAMPLE:
247236
if not isinstance(result, cirq_result):
248237
raise ValueError(
249-
f"result must be a cirq_result for job type {job.job_type}"
238+
f"result: {type(result)}, must be a cirq_result for job type {job.job_type}"
250239
)
251240
return extract_result_SAMPLE(result, job, device)
252241
elif job.job_type == JobType.STATE_VECTOR:
253242
if not isinstance(result, StateVectorTrialResult):
254243
raise ValueError(
255-
f"result must be a cirq_result for job type {job.job_type}"
244+
f"result: {type(result)}, must be a cirq_result for job type {job.job_type}"
256245
)
257246
return extract_result_STATE_VECTOR(result, job, device)
258247
elif job.job_type == JobType.OBSERVABLE:
259248
if isinstance(result, cirq_result):
260249
raise ValueError(
261-
f"result must be a list[float] | list[ObservableMeasuredResult] for job type {job.job_type}"
250+
f"result: {type(result)}, must be a list[float] | list[ObservableMeasuredResult] for job type {job.job_type}"
262251
)
263252
return extract_result_OBSERVABLE(result, job, device)
264253
else:

mpqp/execution/providers/ibm.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ def compute_expectation_value(
7878
)
7979
nb_shots = job.measure.shots
8080
qiskit_observable = job.measure.observable.to_other_language(Language.QISKIT)
81-
if not isinstance(qiskit_observable, Operator):
82-
raise ValueError("qiskit_observable must be an Operator")
81+
assert isinstance(qiskit_observable, Operator)
8382

8483
if nb_shots != 0:
8584
assert ibm_backend is not None
@@ -253,8 +252,7 @@ def submit_ibmq(job: Job) -> tuple[str, RuntimeJob | IBMJob]:
253252
assert isinstance(job.measure, ExpectationMeasure)
254253
estimator = Runtime_Estimator(session=session)
255254
qiskit_observable = job.measure.observable.to_other_language(Language.QISKIT)
256-
if not isinstance(qiskit_observable, Operator):
257-
raise ValueError("qiskit_observable must be an Operator")
255+
assert isinstance(qiskit_observable, Operator)
258256

259257
ibm_job = estimator.run(
260258
qiskit_circuit, qiskit_observable, shots=job.measure.shots

0 commit comments

Comments
 (0)