Skip to content

Commit dfa4db2

Browse files
fix: refactoring and making tests pass
1 parent 06c5abf commit dfa4db2

File tree

10 files changed

+36
-39
lines changed

10 files changed

+36
-39
lines changed

mpqp/core/circuit.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ def measurements(self) -> list[Measure]:
906906
... ])
907907
>>> circuit.measurements # doctest: +NORMALIZE_WHITESPACE
908908
[BasisMeasure(shots=1000),
909-
ExpectationMeasure(Observable(array([[1.+0.j, 0.+0.j], [0.+0.j, 1.+0.j]], dtype=complex64)), [1], shots=1000)]
909+
ExpectationMeasure([Observable(array([[1.+0.j, 0.+0.j], [0.+0.j, 1.+0.j]], dtype=complex64))], [1], shots=1000)]
910910
911911
"""
912912
return [inst for inst in self.instructions if isinstance(inst, Measure)]

mpqp/core/instruction/measurement/expectation_value.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ def __init__(self, observable: Matrix | list[Real] | PauliString):
125125
def matrix(self) -> Matrix:
126126
"""The matrix representation of the observable."""
127127
if self._matrix is None:
128-
if self.is_diagonal:
129-
self._matrix = np.diag(self.diagonal_elements)
128+
if self.is_diagonal and self._diag_elements is not None:
129+
self._matrix = np.diag(self._diag_elements)
130130
else:
131131
self._matrix = self.pauli_string.to_matrix()
132132
matrix = copy.deepcopy(self._matrix).astype(np.complex64)
@@ -301,10 +301,10 @@ def __init__(
301301

302302
super().__init__(targets, shots, label)
303303
# TODO Do some checks on the observables when they are many (same size because of targets)
304-
self.observable: list[Observable]
304+
self.observables: list[Observable]
305305
"""See parameter description."""
306306
if isinstance(observable, Observable):
307-
self.observable = [observable]
307+
self.observables = [observable]
308308
else:
309309
if not all(
310310
observable[0].nb_qubits == obs.nb_qubits for obs in observable[1:]
@@ -313,7 +313,7 @@ def __init__(
313313
"All observables in ExpectationMeasure must have the same size. Sizes: "
314314
+ str([o.nb_qubits for o in observable])
315315
)
316-
self.observable = observable
316+
self.observables = observable
317317
self._check_targets_order()
318318

319319
def _check_targets_order(self):
@@ -324,10 +324,10 @@ def _check_targets_order(self):
324324
self.pre_measure = QCircuit(0)
325325
return
326326

327-
if self.nb_qubits != self.observable[0].nb_qubits:
327+
if self.nb_qubits != self.observables[0].nb_qubits:
328328
raise NumberQubitsError(
329329
f"Target size {self.nb_qubits} doesn't match observable size "
330-
f"{self.observable[0].nb_qubits}."
330+
f"{self.observables[0].nb_qubits}."
331331
)
332332

333333
self.pre_measure = QCircuit(max(self.targets) + 1)
@@ -384,8 +384,7 @@ def __repr__(self) -> str:
384384
)
385385
shots = "" if self.shots == 0 else f", shots={self.shots}"
386386
label = "" if self.label is None else f", label={self.label}"
387-
# TODO: update the repr when self.observable contains a list of observables, with also the number of observables
388-
return f"ExpectationMeasure({self.observable}{targets}{shots}{label})"
387+
return f"ExpectationMeasure({self.observables}{targets}{shots}{label})"
389388

390389
def to_other_language(
391390
self,

mpqp/core/instruction/measurement/pauli_string.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,11 @@ def commutes_with(self, p: PauliString):
338338
339339
Returns:
340340
341+
Examples:
342+
>>> from mpqp.measures import I, X, Y, Z
343+
341344
"""
345+
pass
342346

343347
@staticmethod
344348
def from_matrix(
@@ -807,14 +811,21 @@ def __hash__(self):
807811
return hash(monomials_as_tuples)
808812

809813
def is_diagonal(self) -> bool:
810-
"""Checks wether this pauli string has a diagonal representation, by checking if only ``I`` and ``Z`` Pauli
814+
"""Checks whether this pauli string has a diagonal representation, by checking if only ``I`` and ``Z`` Pauli
811815
operators appears in the monomials of the string.
812-
TODO
813816
814817
Returns:
815818
True if the observable represented by pauli string is diagonal.
819+
820+
Examples:
821+
>>> from mpqp.measures import I, X, Y, Z
822+
>>> (I @ X + Z @ Y - Y @ X).is_diagonal()
823+
False
824+
>>> (I @ Z @ I - 2* Z @ Z @ Z + I @ I @ I).is_diagonal()
825+
True
826+
816827
"""
817-
pass
828+
return all([all([a != X and a != Y for a in m.atoms]) for m in self.monomials])
818829

819830

820831
@typechecked
@@ -943,6 +954,7 @@ def commutes_with(self, pm: PauliStringMonomial) -> bool:
943954
True if this Pauli monomial commutes with the one in parameter.
944955
945956
Examples:
957+
>>> from mpqp.measures import I, X, Y, Z
946958
>>> (I @ X @ Y).commutes_with(Z @ Y @ X)
947959
True
948960
>>> (X @ Z @ Z).commutes_with(Y @ Z @ I)
@@ -1169,6 +1181,7 @@ def commutes_with(self, a: PauliStringAtom) -> bool:
11691181
True if the atoms commute, False otherwise.
11701182
11711183
Examples:
1184+
>>> from mpqp.measures import I, X, Y, Z
11721185
>>> X.commutes_with(X)
11731186
True
11741187
>>> X.commutes_with(Y)

mpqp/execution/providers/atos.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def generate_observable_job(myqlm_circuit: "Circuit", job: Job) -> "JobQLM":
252252
# TODO: [multi-obs] update this to take into account the case when we have list of Observables
253253
if TYPE_CHECKING:
254254
assert job.measure is not None and isinstance(job.measure, ExpectationMeasure)
255-
qlm_obs = job.measure.observable.to_other_language(Language.MY_QLM)
255+
qlm_obs = job.measure.observables[0].to_other_language(Language.MY_QLM)
256256
myqlm_job = myqlm_circuit.to_job(
257257
job_type="OBS",
258258
observable=qlm_obs,

mpqp/execution/providers/aws.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def submit_job_braket(job: Job) -> tuple[str, "QuantumTask"]:
188188
# TODO : [multi-obs] update this to take into account the case when we have list of Observables
189189
if TYPE_CHECKING:
190190
assert isinstance(job.measure, ExpectationMeasure)
191-
herm_op = job.measure.observable.to_other_language(Language.BRAKET)
191+
herm_op = job.measure.observables[0].to_other_language(Language.BRAKET)
192192
braket_circuit.expectation( # pyright: ignore[reportAttributeAccessIssue]
193193
observable=herm_op, target=job.measure.targets
194194
)

mpqp/execution/providers/google.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def run_local(job: Job) -> Result:
164164
assert isinstance(job.measure, ExpectationMeasure)
165165
# TODO: update this to take into account the case when we have list of Observables
166166
# TODO: check if Cirq allows for a list of observable when computing expectation values (apparently yes)
167-
cirq_obs = job.measure.observable.to_other_language(
167+
cirq_obs = job.measure.observables[0].to_other_language(
168168
language=Language.CIRQ, circuit=cirq_circuit
169169
)
170170
if TYPE_CHECKING:
@@ -253,7 +253,7 @@ def run_local_processor(job: Job) -> Result:
253253
assert isinstance(job.measure, ExpectationMeasure)
254254

255255
# TODO: update this to take into account the case when we have list of Observables
256-
cirq_obs = job.measure.observable.to_other_language(
256+
cirq_obs = job.measure.observables[0].to_other_language(
257257
language=Language.CIRQ, circuit=cirq_circuit
258258
)
259259
if TYPE_CHECKING:

mpqp/execution/providers/ibm.py

+2-15
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,8 @@ def compute_expectation_value(
9797
)
9898

9999
nb_shots = job.measure.shots
100-
observables = (
101-
job.measure.observable
102-
if isinstance(job.measure.observable, list)
103-
else [job.measure.observable]
104-
)
105100

106-
qiskit_observables = []
107-
for obs in observables:
108-
if isinstance(obs, Observable):
109-
qiskit_observables.append(obs.to_other_language(Language.QISKIT))
110-
else:
111-
raise ValueError("Unsupported observable")
101+
qiskit_observables = [obs.to_other_language(Language.QISKIT) for obs in job.measure.observables]
112102

113103
if TYPE_CHECKING:
114104
assert all(isinstance(obs, SparsePauliOp) for obs in qiskit_observables)
@@ -546,11 +536,8 @@ def submit_remote_ibm(job: Job) -> tuple[str, "RuntimeJobV2"]:
546536
if TYPE_CHECKING:
547537
assert isinstance(meas, ExpectationMeasure)
548538
estimator = Runtime_Estimator(mode=session)
549-
qiskit_observables = (
550-
meas.observable if isinstance(meas.observable, list) else [meas.observable]
551-
)
552539
qiskit_observables = [
553-
obs.to_other_language(Language.QISKIT) for obs in qiskit_observables
540+
obs.to_other_language(Language.QISKIT) for obs in meas.observables
554541
]
555542
if TYPE_CHECKING:
556543
assert all(isinstance(obs, SparsePauliOp) for obs in qiskit_observables)

mpqp/execution/runner.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def adjust_measure(measure: ExpectationMeasure, circuit: QCircuit):
7878

7979
tweaked_observables = [
8080
Observable(np.kron(np.kron(Id_before, obs.matrix), Id_after))
81-
for obs in measure.observable
81+
for obs in measure.observables
8282
]
8383

8484
tweaked_measure = ExpectationMeasure(
@@ -123,13 +123,11 @@ def generate_job(
123123
else:
124124
job = Job(JobType.SAMPLE, circuit, device, measurement)
125125
elif isinstance(measurement, ExpectationMeasure):
126-
adjusted_m = adjust_measure(measurement, circuit)
127-
print(adjusted_m.observable[0].nb_qubits)
128126
job = Job(
129127
JobType.OBSERVABLE,
130128
circuit,
131129
device,
132-
adjusted_m,
130+
adjust_measure(measurement, circuit),
133131
)
134132
else:
135133
raise NotImplementedError(

tests/core/test_circuit.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def test_count(circuit: QCircuit, filter: tuple[type[Gate]], count: int):
191191
]
192192
),
193193
"[BasisMeasure([0, 1], shots=1000), ExpectationMeasure("
194-
"Observable(array([[1.+0.j, 0.+0.j], [0.+0.j, 1.+0.j]], dtype=complex64)), [1], shots=1000)]",
194+
"[Observable(array([[1.+0.j, 0.+0.j], [0.+0.j, 1.+0.j]], dtype=complex64))], [1], shots=1000)]",
195195
)
196196
],
197197
)

tests/execution/test_runner.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ def test_adjust_measure(
3333
measure = ExpectationMeasure(Observable(obs_matrix), measure_targets)
3434
adjusted_observable_matrix = np.kron(
3535
np.kron(
36-
np.eye(2**nb_ids_before, dtype=np.complex64), measure.observable.matrix
36+
np.eye(2**nb_ids_before, dtype=np.complex64), measure.observables[0].matrix
3737
),
3838
np.eye(2**nb_ids_after),
3939
)
4040
assert matrix_eq(
41-
adjust_measure(measure, circuit).observable.matrix, adjusted_observable_matrix
41+
adjust_measure(measure, circuit).observables[0].matrix, adjusted_observable_matrix
4242
)

0 commit comments

Comments
 (0)