@@ -16,7 +16,7 @@ class to define your observable, and a :class:`ExpectationMeasure` to perform
16
16
from typeguard import typechecked
17
17
18
18
if TYPE_CHECKING :
19
- import cirq .circuits .circuit as Cirq_Circuit
19
+ from cirq .circuits .circuit import Circuit as Cirq_Circuit
20
20
21
21
from mpqp .core .instruction .gates .native_gates import SWAP
22
22
from mpqp .core .instruction .measurement .measure import Measure
@@ -40,9 +40,8 @@ class Observable:
40
40
>>> obs = Observable(matrix)
41
41
>>> obs2 = Observable(pauli_string)
42
42
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.
46
45
47
46
Raises:
48
47
ValueError: If the input matrix is not Hermitian or does not have a square shape.
@@ -52,33 +51,17 @@ class Observable:
52
51
"""
53
52
54
53
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
70
54
self ._matrix = None
71
55
self ._pauli_string = None
72
56
73
57
if isinstance (observable , PauliString ):
74
58
self .nb_qubits = observable .nb_qubits
75
59
self ._pauli_string = observable .simplify ()
76
- self .observable = self ._pauli_string
77
60
else :
78
61
self .nb_qubits = int (np .log2 (len (observable )))
79
62
"""Number of qubits of this observable."""
80
63
self ._matrix = np .array (observable )
81
-
64
+
82
65
basis_states = 2 ** self .nb_qubits
83
66
if self .matrix .shape != (basis_states , basis_states ):
84
67
raise ValueError (
@@ -99,14 +82,11 @@ def matrix(self) -> Matrix:
99
82
100
83
Returns:
101
84
np.ndarray: The matrix representation of the observable.
102
-
103
85
"""
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
110
90
111
91
@property
112
92
def pauli_string (self ) -> PauliString :
@@ -116,15 +96,23 @@ def pauli_string(self) -> PauliString:
116
96
Returns:
117
97
PauliString: The PauliString representation of the observable.
118
98
"""
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
125
113
126
114
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 )} )"
128
116
129
117
def __mult__ (self , other : Expr | Complex ) -> Observable :
130
118
"""3M-TODO"""
@@ -136,83 +124,67 @@ def subs(
136
124
"""3M-TODO"""
137
125
...
138
126
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
+ ):
198
130
"""
199
131
Converts the observable to the representation of another quantum programming language.
200
132
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:
202
140
language (str): The target programming language ('qiskit', 'pyquil', 'braket', 'cirq').
203
141
circuit: The Cirq circuit associated with the observable (required for 'cirq' language).
204
142
205
143
Returns:
206
144
Depends on the target language.
207
145
"""
208
146
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 )
212
150
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 )
214
154
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
216
188
else :
217
189
raise ValueError (f"Unsupported language: { language } " )
218
190
@@ -263,7 +235,7 @@ def __init__(
263
235
super ().__init__ (targets , shots , label )
264
236
self .observable = observable
265
237
"""See parameter description."""
266
- # TODO Check
238
+ # Raise an error if the number of target qubits does not match the size of the observable.
267
239
if self .nb_qubits != observable .nb_qubits :
268
240
raise NumberQubitsError (
269
241
f"{ self .nb_qubits } , the number of target qubit(s) doesn't match"
@@ -316,6 +288,8 @@ def to_other_language(
316
288
) -> None :
317
289
if qiskit_parameters is None :
318
290
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
319
293
if language == Language .QISKIT :
320
294
raise NotImplementedError (
321
295
"Qiskit does not implement these kind of measures"
0 commit comments