Skip to content

Commit 13dfc15

Browse files
Merge branch 'dev' into feat-cirq-provider
2 parents 76b3ec6 + 38365ca commit 13dfc15

File tree

9 files changed

+290
-209
lines changed

9 files changed

+290
-209
lines changed

examples/scripts/observable_job.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from mpqp.execution import run
77
from mpqp.measures import ExpectationMeasure, Observable
88
from mpqp.execution.devices import ATOSDevice, IBMDevice, AWSDevice, GOOGLEDevice
9+
from mpqp.core.instruction.measurement.pauli_string import I, Z
910

1011
obs = Observable(
1112
np.array(
@@ -19,18 +20,12 @@
1920
)
2021
)
2122

22-
obs2 = Observable(
23-
np.array(
24-
[
25-
[3.0, 0.0, 0.0, 0.0],
26-
[0.0, 1.0, 0.0, 0.0],
27-
[0.0, 0.0, -1.0, 0.0],
28-
[0.0, 0.0, 0.0, 0.0],
29-
],
30-
dtype=float,
31-
)
32-
)
23+
obs2 = Observable(1 * I @ Z + 1 * I @ I)
3324

25+
# Observable can be constructed from a Pauli string or a matrix
26+
print("Observable2:")
27+
print(obs2.pauli_string)
28+
print(obs2.matrix)
3429

3530
# Declaration of the circuit with the right size
3631
circuit = QCircuit(2, label="Observable test")

mpqp/core/instruction/measurement/expectation_value.py

+75-101
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class to define your observable, and a :class:`ExpectationMeasure` to perform
1616
from typeguard import typechecked
1717

1818
if TYPE_CHECKING:
19-
import cirq.circuits.circuit as Cirq_Circuit
19+
from cirq.circuits.circuit import Circuit as Cirq_Circuit
2020

2121
from mpqp.core.instruction.gates.native_gates import SWAP
2222
from mpqp.core.instruction.measurement.measure import Measure
@@ -40,9 +40,8 @@ class Observable:
4040
>>> obs = Observable(matrix)
4141
>>> obs2 = Observable(pauli_string)
4242
43-
Attributes:
44-
matrix: Hermitian matrix representing the observable.
45-
pauli_string: PauliString representing the observable.
43+
Args:
44+
observable : can be either a Hermitian matrix representing the observable or PauliString representing the observable.
4645
4746
Raises:
4847
ValueError: If the input matrix is not Hermitian or does not have a square shape.
@@ -52,33 +51,17 @@ class Observable:
5251
"""
5352

5453
def __init__(self, observable: Matrix | PauliString):
55-
"""
56-
Initialize an Observable object.
57-
58-
Args:
59-
observable (Matrix | PauliString): The observable to be represented.
60-
Can be specified as a matrix or a PauliString.
61-
62-
Raises:
63-
ValueError: If the input matrix is not hermitian or does not have a
64-
square shape.
65-
NumberQubitsError: If the number of qubits in the input observable does
66-
not match the number of target qubits.
67-
68-
"""
69-
self.observable = observable
7054
self._matrix = None
7155
self._pauli_string = None
7256

7357
if isinstance(observable, PauliString):
7458
self.nb_qubits = observable.nb_qubits
7559
self._pauli_string = observable.simplify()
76-
self.observable = self._pauli_string
7760
else:
7861
self.nb_qubits = int(np.log2(len(observable)))
7962
"""Number of qubits of this observable."""
8063
self._matrix = np.array(observable)
81-
64+
8265
basis_states = 2**self.nb_qubits
8366
if self.matrix.shape != (basis_states, basis_states):
8467
raise ValueError(
@@ -99,14 +82,11 @@ def matrix(self) -> Matrix:
9982
10083
Returns:
10184
np.ndarray: The matrix representation of the observable.
102-
10385
"""
104-
if isinstance(self.observable, PauliString):
105-
if self._matrix is None:
106-
self._matrix = self.observable.to_matrix()
107-
return self._matrix
108-
else:
109-
return self.observable
86+
if self._matrix is None:
87+
self._matrix = self.pauli_string.to_matrix()
88+
matrix = copy.deepcopy(self._matrix).astype(np.complex64)
89+
return matrix
11090

11191
@property
11292
def pauli_string(self) -> PauliString:
@@ -116,15 +96,23 @@ def pauli_string(self) -> PauliString:
11696
Returns:
11797
PauliString: The PauliString representation of the observable.
11898
"""
119-
if isinstance(self.observable, PauliString):
120-
return self.observable
121-
else:
122-
if self._pauli_string is None:
123-
self._pauli_string = PauliString.from_matrix(self.matrix)
124-
return self._pauli_string
99+
if self._pauli_string is None:
100+
self._pauli_string = PauliString.from_matrix(self.matrix)
101+
pauli_string = copy.deepcopy(self._pauli_string)
102+
return pauli_string
103+
104+
@pauli_string.setter
105+
def pauli_string(self, pauli_string: PauliString):
106+
self._pauli_string = pauli_string
107+
self._matrix = None
108+
109+
@matrix.setter
110+
def matrix(self, matrix: Matrix):
111+
self._matrix = matrix
112+
self._pauli_string = None
125113

126114
def __repr__(self) -> str:
127-
return f"{type(self).__name__}({one_lined_repr(self.observable)})"
115+
return f"{type(self).__name__}({one_lined_repr(self.matrix)})"
128116

129117
def __mult__(self, other: Expr | Complex) -> Observable:
130118
"""3M-TODO"""
@@ -136,83 +124,67 @@ def subs(
136124
"""3M-TODO"""
137125
...
138126

139-
def to_qiskit_observable(self):
140-
from qiskit.quantum_info import Operator
141-
142-
return Operator(self.matrix)
143-
144-
def to_myqlm_observable(self):
145-
from qat.core.wrappers.observable import Observable as QLM_Observable
146-
147-
return QLM_Observable(self.nb_qubits, matrix=self.matrix)
148-
149-
def to_braket_observable(self):
150-
from braket.circuits.observables import Hermitian
151-
152-
return Hermitian(self.matrix)
153-
154-
def to_cirq_observable(self, cirq_circuit: Cirq_Circuit):
155-
"""
156-
Convert the observable to a Cirq observable.
157-
158-
Args:
159-
cirq_circuit (cirq.Circuit): The circuit to which the Cirq observable
160-
will be added.
161-
162-
Returns:
163-
cirq.PauliSum: The Cirq observable.
164-
165-
Raises:
166-
ValueError: If the circuit is not specified.
167-
168-
"""
169-
from cirq import I as Cirq_I, X as Cirq_X, Y as Cirq_Y, Z as Cirq_Z
170-
171-
if cirq_circuit is None:
172-
raise ValueError("Circuit must be specified for cirq_observable.")
173-
174-
all_qubits = set(
175-
q for moment in cirq_circuit for op in moment.operations for q in op.qubits
176-
)
177-
all_qubits_list = sorted(all_qubits)
178-
179-
cirq_pauli_string = None
180-
pauli_gate_map = {"I": Cirq_I, "X": Cirq_X, "Y": Cirq_Y, "Z": Cirq_Z}
181-
for monomial in self.pauli_string.monomials:
182-
cirq_monomial = None
183-
for index, atom in enumerate(monomial.atoms):
184-
cirq_atom = pauli_gate_map[atom.label](all_qubits_list[index])
185-
cirq_monomial = (
186-
cirq_atom if cirq_monomial is None else cirq_monomial * cirq_atom
187-
)
188-
cirq_monomial *= monomial.coef
189-
cirq_pauli_string = (
190-
cirq_monomial
191-
if cirq_pauli_string is None
192-
else cirq_pauli_string + cirq_monomial
193-
)
194-
195-
return cirq_pauli_string
196-
197-
def to_other_language(self, language: Language, circuit=None):
127+
def to_other_language(
128+
self, language: Language, circuit: Optional[Cirq_Circuit] = None
129+
):
198130
"""
199131
Converts the observable to the representation of another quantum programming language.
200132
201-
Parameters:
133+
Example:
134+
>>> obs = Observable(np.diag([0.7, -1, 1, 1]))
135+
>>> obs_qiskit = obs.to_other_language(Language.QISKIT)
136+
>>> print(obs_qiskit)
137+
<bound method Observable.to_qiskit_observable of Observable(array([[ 0.7, 0. , 0. , 0. ], [ 0. , -1. , 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]]))>
138+
139+
Args:
202140
language (str): The target programming language ('qiskit', 'pyquil', 'braket', 'cirq').
203141
circuit: The Cirq circuit associated with the observable (required for 'cirq' language).
204142
205143
Returns:
206144
Depends on the target language.
207145
"""
208146
if language == Language.QISKIT:
209-
return self.to_qiskit_observable()
210-
elif language == Language.CIRQ:
211-
return self.to_cirq_observable(circuit)
147+
from qiskit.quantum_info import Operator
148+
149+
return Operator(self.matrix)
212150
elif language == Language.MY_QLM:
213-
return self.to_myqlm_observable()
151+
from qat.core.wrappers.observable import Observable as QLM_Observable
152+
153+
return QLM_Observable(self.nb_qubits, matrix=self.matrix)
214154
elif language == Language.BRAKET:
215-
return self.to_braket_observable()
155+
from braket.circuits.observables import Hermitian
156+
157+
return Hermitian(self.matrix)
158+
elif language == Language.CIRQ:
159+
if circuit is None:
160+
raise ValueError("Circuit must be specified for cirq_observable.")
161+
from cirq.ops.identity import I as Cirq_I
162+
from cirq.ops.pauli_gates import X as Cirq_X, Y as Cirq_Y, Z as Cirq_Z
163+
164+
all_qubits = set(
165+
q for moment in circuit for op in moment.operations for q in op.qubits
166+
)
167+
all_qubits_list = sorted(all_qubits)
168+
169+
cirq_pauli_string = None
170+
pauli_gate_map = {"I": Cirq_I, "X": Cirq_X, "Y": Cirq_Y, "Z": Cirq_Z}
171+
for monomial in self.pauli_string.monomials:
172+
cirq_monomial = None
173+
for index, atom in enumerate(monomial.atoms):
174+
cirq_atom = pauli_gate_map[atom.label](all_qubits_list[index])
175+
cirq_monomial = (
176+
cirq_atom
177+
if cirq_monomial is None
178+
else cirq_monomial * cirq_atom
179+
)
180+
cirq_monomial *= monomial.coef
181+
cirq_pauli_string = (
182+
cirq_monomial
183+
if cirq_pauli_string is None
184+
else cirq_pauli_string + cirq_monomial
185+
)
186+
187+
return cirq_pauli_string
216188
else:
217189
raise ValueError(f"Unsupported language: {language}")
218190

@@ -263,7 +235,7 @@ def __init__(
263235
super().__init__(targets, shots, label)
264236
self.observable = observable
265237
"""See parameter description."""
266-
# TODO Check
238+
# Raise an error if the number of target qubits does not match the size of the observable.
267239
if self.nb_qubits != observable.nb_qubits:
268240
raise NumberQubitsError(
269241
f"{self.nb_qubits}, the number of target qubit(s) doesn't match"
@@ -316,6 +288,8 @@ def to_other_language(
316288
) -> None:
317289
if qiskit_parameters is None:
318290
qiskit_parameters = set()
291+
#TODO : incoherence here, if the language is Qiskit we raise a NotImplementedError, and otherwise we say that
292+
# only qiskit is supported
319293
if language == Language.QISKIT:
320294
raise NotImplementedError(
321295
"Qiskit does not implement these kind of measures"

0 commit comments

Comments
 (0)