tequila-basic 1.9.7__py3-none-any.whl → 1.9.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,296 +1,413 @@
1
1
  from __future__ import annotations
2
- import copy
2
+
3
3
  import typing
4
- from typing import Dict, Union
4
+ from copy import deepcopy
5
+ from math import log2
6
+ from typing import Union, Generator
7
+
5
8
  import numpy
9
+ import numpy as np
10
+ import numpy.typing as npt
6
11
  import numbers
7
12
 
8
- from tequila.utils.bitstrings import BitNumbering, BitString, initialize_bitstring
9
- from tequila import TequilaException
10
- from tequila.utils.keymap import KeyMapLSB2MSB, KeyMapMSB2LSB
11
- from tequila.tools import number_to_string
13
+ import sympy
12
14
 
13
- # from __future__ import annotations # can use that in python 3.7+ to get rid of string type hints
15
+ from tequila.utils.bitstrings import BitString, reverse_int_bits
16
+ from tequila import TequilaException, BitNumbering, initialize_bitstring
17
+ from tequila.utils.keymap import KeyMapABC
14
18
 
15
19
  if typing.TYPE_CHECKING:
16
- # don't need those structures, just for convenient type hinting
20
+ # Don't need those structures, just for convenient type hinting
17
21
  from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian, PauliString
18
22
 
19
23
 
20
24
  class QubitWaveFunction:
21
25
  """
22
- Store Wavefunction as dictionary of comp. basis state and complex numbers
23
- Use the same structure for Measurments results with int instead of complex numbers (counts)
26
+ Represents a wavefunction.
27
+ Amplitudes are either stored in a Numpy array for dense wavefunctions, or a dictionary for sparse wavefunctions.
28
+ Does not enforce normalization.
24
29
  """
25
30
 
26
- numbering = BitNumbering.MSB
27
-
28
- def apply_keymap(self, keymap, initial_state: BitString = None):
29
- self.n_qubits = keymap.n_qubits
30
- mapped_state = dict()
31
- for k, v in self.state.items():
32
- mapped_key=keymap(input_state=k, initial_state=initial_state)
33
- if mapped_key in mapped_state:
34
- mapped_state[mapped_key] += v
35
- else:
36
- mapped_state[mapped_key] = v
37
-
38
- self.state = mapped_state
39
- return self
31
+ def __init__(self, n_qubits: int, numbering: BitNumbering = BitNumbering.MSB, dense: bool = False,
32
+ init_state: bool = True) -> None:
33
+ """
34
+ Initialize a QubitWaveFunction with all amplitudes set to zero.
40
35
 
41
- @property
42
- def n_qubits(self) -> int:
43
- if self._n_qubits is None:
44
- return self.min_qubits()
36
+ :param n_qubits: Number of qubits.
37
+ :param numbering: Whether the first qubit is the most or least significant.
38
+ :param dense: Whether to store the amplitudes in a Numpy array instead of a dictionary.
39
+ :param init_state: Whether to initialize the state array.
40
+ If False, set_state must be called immediately after the constructor.
41
+ """
42
+ self._n_qubits: int = n_qubits
43
+ self._numbering = numbering
44
+ self._dense = dense
45
+ if init_state:
46
+ self._state = np.zeros(2 ** self._n_qubits, dtype=complex) if dense else dict()
45
47
  else:
46
- return max(self._n_qubits, self.min_qubits())
48
+ self._state = None
47
49
 
48
- def min_qubits(self) -> int:
49
- if len(self.state) > 0:
50
- maxk = max(self.state.keys())
51
- return maxk.nbits
50
+ @classmethod
51
+ def from_wavefunction(cls, wfn: QubitWaveFunction, keymap: KeyMapABC = None, n_qubits: int = None,
52
+ initial_state: BitString = None) -> QubitWaveFunction:
53
+ """
54
+ Create a copy of a wavefunction.
55
+
56
+ :param wfn: The wavefunction to copy.
57
+ :param keymap: A keymap to apply to the wavefunction.
58
+ :param n_qubits: Number of qubits of the new wavefunction.
59
+ Must not be None if keymap is not None.
60
+ :param initial_state: Initial state to pass to the keymap.
61
+ :return: The copied wavefunction.
62
+ """
63
+ if keymap is not None:
64
+ result = QubitWaveFunction(n_qubits, numbering=wfn._numbering, dense=wfn._dense)
65
+ # Change amplitudes to sympy objects
66
+ if wfn._dense and wfn._state.dtype == object:
67
+ result = sympy.Integer(1) * result
68
+ for index, coeff in wfn.raw_items():
69
+ key = initialize_bitstring(index, wfn._n_qubits, numbering_in=wfn._numbering, numbering_out=keymap.numbering)
70
+ key = keymap(key, initial_state)
71
+ result[key] += coeff
72
+ return result
52
73
  else:
53
- return 0
74
+ return deepcopy(wfn)
54
75
 
55
- @n_qubits.setter
56
- def n_qubits(self, n_qubits):
57
- if n_qubits is not None:
58
- self._n_qubits = max(n_qubits, self.min_qubits())
59
- return self
76
+ @classmethod
77
+ def from_array(cls, array: npt.NDArray[complex], numbering: BitNumbering = BitNumbering.MSB,
78
+ copy: bool = True) -> QubitWaveFunction:
79
+ """
80
+ Create a dense wavefunction from a Numpy array.
60
81
 
61
- @property
62
- def state(self):
63
- if self._state is None:
64
- return dict()
65
- else:
66
- return self._state
67
-
68
- @state.setter
69
- def state(self, other: Dict[BitString, complex]):
70
- assert (isinstance(other, dict))
71
- self._state = other
72
-
73
- def __init__(self, state: Dict[BitString, complex] = None, n_qubits=None):
74
- if state is None:
75
- self._state = dict()
76
- elif isinstance(state, int):
77
- self._state = self.from_int(i=state, n_qubits=n_qubits).state
78
- elif isinstance(state, str):
79
- self._state = self.from_string(string=state, n_qubits=n_qubits).state
80
- elif isinstance(state, numpy.ndarray) or isinstance(state, list):
81
- self._state = self.from_array(arr=state, n_qubits=n_qubits).state
82
- elif hasattr(state, "state"):
83
- self._state = state.state
84
- else:
85
- self._state = state
86
- self._n_qubits = n_qubits
82
+ :param array: Array of amplitudes.
83
+ :param numbering: Whether the first qubit is the most or least significant.
84
+ :param copy: Whether to copy the array or use it directly.
85
+ If False, the array must not be modified after the constructor.
86
+ :return: The created wavefunction.
87
+ """
88
+ if not log2(len(array)).is_integer():
89
+ raise ValueError(f"Array length must be a power of 2, received {len(array)}")
90
+ n_qubits = int(log2(len(array)))
91
+ result = QubitWaveFunction(n_qubits, numbering, dense=True, init_state=False)
92
+ result.set_state(array, copy)
93
+ return result
87
94
 
88
- def items(self):
89
- return self.state.items()
95
+ @classmethod
96
+ def from_basis_state(cls, n_qubits: int, basis_state: Union[int, BitString],
97
+ numbering: BitNumbering = BitNumbering.MSB) -> QubitWaveFunction:
98
+ """
99
+ Create a sparse wavefunction that is a basis state.
90
100
 
91
- def keys(self):
92
- return self.state.keys()
101
+ :param n_qubits: Number of qubits.
102
+ :param basis_state: Index of the basis state.
103
+ :param numbering: Whether the first qubit is the most or least significant.
104
+ :return: The created wavefunction.
105
+ """
106
+ if 2 ** n_qubits <= basis_state:
107
+ raise ValueError(f"Number of qubits {n_qubits} insufficient for basis state {basis_state}")
108
+ if isinstance(basis_state, BitString):
109
+ basis_state = reverse_int_bits(basis_state.integer,
110
+ basis_state.nbits) if numbering != basis_state.numbering else basis_state.integer
111
+ result = QubitWaveFunction(n_qubits, numbering)
112
+ result[basis_state] = 1.0
113
+ return result
93
114
 
94
- def values(self):
95
- return self.state.values()
115
+ @classmethod
116
+ def from_string(cls, string: str, numbering: BitNumbering = BitNumbering.MSB) -> QubitWaveFunction:
117
+ """
118
+ Create a sparse wavefunction from a string.
119
+
120
+ :param string: String representation of the wavefunction.
121
+ :param numbering: Whether the first qubit is the most or least significant.
122
+ :return: The created wavefunction.
123
+ """
124
+ try:
125
+ string = string.replace(" ", "")
126
+ string = string.replace("*", "")
127
+ terms = string.split(">")[:-1]
128
+ n_qubits = len(terms[0].split("|")[-1])
129
+ result = QubitWaveFunction(n_qubits, numbering)
130
+ for term in terms:
131
+ coeff, index = term.split("|")
132
+ coeff = complex(coeff) if coeff != "" else 1.0
133
+ index = int(index, 2)
134
+ result[index] = coeff
135
+ return result
136
+ except ValueError:
137
+ raise TequilaException(f"Failed to initialize QubitWaveFunction from string:\n\"{string}\"\n")
138
+
139
+ @classmethod
140
+ def convert_from(cls, n_qubits: int, val: Union[QubitWaveFunction, int, str, numpy.ndarray]):
141
+ """
142
+ Convert a value to a QubitWaveFunction.
143
+ Accepts QubitWaveFunction, int, str, and numpy.ndarray.
96
144
 
97
- @staticmethod
98
- def convert_bitstring(key: Union[BitString, numbers.Integral], n_qubits):
99
- if isinstance(key, numbers.Integral):
100
- return BitString.from_int(integer=key, nbits=n_qubits)
101
- elif isinstance(key, str):
102
- return BitString.from_binary(binary=key, nbits=n_qubits)
145
+ :param n_qubits: Number of qubits.
146
+ :param val: Value to convert.
147
+ :return: The converted value.
148
+ """
149
+ if isinstance(val, QubitWaveFunction):
150
+ return val
151
+ elif isinstance(val, int):
152
+ return cls.from_basis_state(n_qubits=n_qubits, basis_state=val)
153
+ elif isinstance(val, str):
154
+ return cls.from_string(val)
155
+ elif isinstance(val, numpy.ndarray):
156
+ return cls.from_array(val)
103
157
  else:
104
- return key
158
+ raise TequilaException(f"Cannot initialize QubitWaveFunction from type {type(val)}")
159
+
160
+ @property
161
+ def n_qubits(self) -> int:
162
+ """
163
+ Returns number of qubits in the wavefunction.
164
+ """
165
+ return self._n_qubits
105
166
 
106
- def __getitem__(self, item: BitString):
107
- key = self.convert_bitstring(item, self.n_qubits)
108
- return self.state[key]
167
+ @property
168
+ def numbering(self) -> BitNumbering:
169
+ """
170
+ Returns the bit numbering of the wavefunction.
171
+ """
172
+ return self._numbering
173
+
174
+ @property
175
+ def dense(self) -> bool:
176
+ """
177
+ Returns whether the wavefunction is dense.
178
+ """
179
+ return self._dense
109
180
 
110
- def __call__(self, key, *args, **kwargs) -> numbers.Number:
181
+ def to_array(self, out_numbering: BitNumbering = BitNumbering.MSB, copy: bool = True) -> npt.NDArray[complex]:
111
182
  """
112
- Like getitem but returns zero if key is not there
113
-
114
- Parameters
115
- ----------
116
- key: bitstring (or int or str)
117
- Returns
118
- -------
119
- Return the amplitude or measurement occurence of a bitstring
183
+ Returns array of amplitudes.
184
+
185
+ :param out_numbering: Whether the first qubit is the most or least significant in the output array indices.
186
+ For dense wavefunctions, this operation is significantly cheaper when this is the same as the numbering
187
+ of the wavefunction.
188
+ :param copy: Whether to copy the array or use it directly for dense Wavefunctions.
189
+ If False, changes to the array or wavefunction will affect each other.
190
+ :return: Array of amplitudes.
120
191
  """
121
- ckey = self.convert_bitstring(key, self.n_qubits)
122
- if ckey in self.state:
123
- return self.state[ckey]
192
+ if self._dense and self._numbering == out_numbering:
193
+ return self._state.copy() if copy else self._state
124
194
  else:
125
- return 0.0
195
+ result = np.zeros(2 ** self._n_qubits, dtype=complex)
196
+ for k, v in self.raw_items():
197
+ if self._numbering != out_numbering:
198
+ k = reverse_int_bits(k, self._n_qubits)
199
+ result[k] = v
200
+ return result
201
+
202
+ def set_state(self, value: npt.NDArray[complex], copy: bool = True) -> None:
203
+ """
204
+ Sets the state to an array.
205
+ After this call, the wavefunction will be dense.
126
206
 
207
+ :param value: Array of amplitudes. Length must be 2 ** n_qubits.
208
+ :param copy: Whether to copy the array or use it directly.
209
+ If False, changes to the array or wavefunction will affect each other.
210
+ """
211
+ if len(value) != 2 ** self._n_qubits:
212
+ raise ValueError(f"Wavefunction of {self._n_qubits} qubits must have {2 ** self._n_qubits} amplitudes, "
213
+ f"received {len(value)}")
214
+ self._dense = True
215
+ if copy:
216
+ self._state = value.copy()
217
+ else:
218
+ self._state = value
219
+
220
+ def __getitem__(self, key: Union[int, BitString]) -> complex:
221
+ if isinstance(key, BitString):
222
+ key = reverse_int_bits(key.integer, key.nbits) if self._numbering != key.numbering else key.integer
223
+ return self._state[key] if self._dense else self._state.get(key, 0)
224
+
225
+ def __setitem__(self, key: Union[int, BitString], value: complex) -> None:
226
+ if isinstance(key, BitString):
227
+ key = reverse_int_bits(key.integer, key.nbits) if self._numbering != key.numbering else key.integer
228
+ self._state[key] = value
229
+
230
+ def __contains__(self, item: Union[int, BitString]) -> bool:
231
+ if isinstance(item, BitString):
232
+ item = reverse_int_bits(item.integer, item.nbits) if self._numbering != item.numbering else item.integer
233
+ return abs(self[item]) > 1e-6
234
+
235
+ def raw_items(self) -> Generator[tuple[int, complex]]:
236
+ """Returns a generator of non-zero amplitudes with integer indices."""
237
+ return ((k, v) for k, v in (enumerate(self._state) if self._dense else self._state.items()))
238
+
239
+ def items(self) -> Generator[tuple[BitString, complex]]:
240
+ """Returns a generator of non-zero amplitudes with BitString indices."""
241
+ return ((initialize_bitstring(k, self._n_qubits, self._numbering), v)
242
+ for k, v in self.raw_items()
243
+ if isinstance(v, sympy.Basic) or abs(v) > 1e-6)
244
+
245
+ def keys(self) -> Generator[BitString]:
246
+ """Returns a generator of BitString indices of non-zero amplitudes."""
247
+ return (k for k, v in self.items())
248
+
249
+ def values(self) -> Generator[complex]:
250
+ """Returns a generator of non-zero amplitudes."""
251
+ return (v for k, v in self.items())
252
+
253
+ def __eq__(self, other) -> bool:
254
+ if not isinstance(other, QubitWaveFunction):
255
+ return False
256
+
257
+ raise TequilaException("Wavefunction equality is not well-defined. Consider using isclose.")
258
+
259
+ def isclose(self: QubitWaveFunction,
260
+ other: QubitWaveFunction,
261
+ rtol: float = 1e-5,
262
+ atol: float = 1e-8) -> bool:
263
+ """
264
+ Check if two wavefunctions are close, up to a global phase.
127
265
 
266
+ :param other: The other wavefunction.
267
+ :param rtol: Relative tolerance.
268
+ :param atol: Absolute tolerance.
269
+ :return: Whether the wavefunctions are close.
270
+ """
271
+ inner = self.inner(other)
272
+ self_norm = self.norm()
273
+ other_norm = other.norm()
274
+ cosine_similarity = inner / (self_norm * other_norm)
128
275
 
129
- def __setitem__(self, key: BitString, value: numbers.Number):
130
- self._state[self.convert_bitstring(key, self.n_qubits)] = value
131
- return self
276
+ return (np.isclose(abs(cosine_similarity), 1.0, rtol, atol)
277
+ and np.isclose(self_norm, other_norm, rtol, atol))
132
278
 
133
- def __contains__(self, item: BitString):
134
- return self.convert_bitstring(item, self.n_qubits) in self.keys()
279
+ def __add__(self, other: QubitWaveFunction) -> QubitWaveFunction:
280
+ if self._dense and other._dense and self._numbering == other._numbering:
281
+ return QubitWaveFunction.from_array(self._state + other._state, self.numbering, copy=False)
282
+ else:
283
+ result = QubitWaveFunction.from_wavefunction(self)
284
+ result += other
285
+ return result
135
286
 
136
- def __len__(self):
137
- return len(self.state)
287
+ def __iadd__(self, other: QubitWaveFunction) -> QubitWaveFunction:
288
+ if self._dense and other._dense and self._numbering == other._numbering:
289
+ self._state += other._state
290
+ else:
291
+ for k, v in other.raw_items():
292
+ if self._numbering != other._numbering:
293
+ k = reverse_int_bits(k, self._n_qubits)
294
+ self[k] += v
295
+ return self
138
296
 
139
- @classmethod
140
- def from_array(cls, arr: numpy.ndarray, keymap=None, threshold: float = 1.e-6,
141
- numbering: BitNumbering = BitNumbering.MSB, n_qubits: int = None):
142
- arr = numpy.asarray(arr)
143
- assert (len(arr.shape) == 1)
144
- state = dict()
145
- maxkey = len(arr) - 1
146
- maxbit = initialize_bitstring(integer=maxkey, numbering_in=numbering, numbering_out=cls.numbering).nbits
147
- for ii, v in enumerate(arr):
148
- if abs(v) > threshold:
149
- i = initialize_bitstring(integer=ii, nbits=maxbit, numbering_in=numbering, numbering_out=cls.numbering)
150
- key = i if keymap is None else keymap(i)
151
- state[key] = v
152
- result = QubitWaveFunction(state, n_qubits=n_qubits)
297
+ def __sub__(self, other: QubitWaveFunction) -> QubitWaveFunction:
298
+ if self._dense and other._dense and self._numbering == other._numbering:
299
+ return QubitWaveFunction.from_array(self._state - other._state, self.numbering, copy=False)
300
+ else:
301
+ result = QubitWaveFunction.from_wavefunction(self)
302
+ result -= other
303
+ return result
153
304
 
154
- return result
305
+ def __isub__(self, other: QubitWaveFunction) -> QubitWaveFunction:
306
+ if self._dense and other._dense and self._numbering == other._numbering:
307
+ self._state -= other._state
308
+ else:
309
+ for k, v in other.raw_items():
310
+ if self._numbering != other._numbering:
311
+ k = reverse_int_bits(k, self._n_qubits)
312
+ self[k] -= v
313
+ return self
155
314
 
156
- @classmethod
157
- def from_int(cls, i: int, coeff=1, n_qubits: int = None):
158
- if isinstance(i, BitString):
159
- return QubitWaveFunction(state={i: coeff}, n_qubits=n_qubits)
315
+ def __rmul__(self, other: complex) -> QubitWaveFunction:
316
+ if self._dense:
317
+ return QubitWaveFunction.from_array(other * self._state, self.numbering, copy=False)
160
318
  else:
161
- return QubitWaveFunction(state={BitString.from_int(integer=i, nbits=n_qubits): coeff}, n_qubits=n_qubits)
319
+ result = QubitWaveFunction.from_wavefunction(self)
320
+ result *= other
321
+ return result
162
322
 
163
- @classmethod
164
- def from_string(cls, string: str, n_qubits: int = None):
323
+ def __imul__(self, other: complex) -> QubitWaveFunction:
324
+ if self._dense:
325
+ self._state *= other
326
+ else:
327
+ for k, v in self.raw_items():
328
+ self[k] = other * v
329
+ return self
330
+
331
+ def inner(self, other: QubitWaveFunction) -> complex:
332
+ """Returns the inner product with another wavefunction."""
333
+ if self._dense and other._dense and self._numbering == other._numbering:
334
+ return np.inner(self._state.conjugate(), other._state)
335
+ else:
336
+ result = 0
337
+ for k, v in self.raw_items():
338
+ if self._numbering != other._numbering:
339
+ k = reverse_int_bits(k, self._n_qubits)
340
+ result += v.conjugate() * other[k]
341
+ if isinstance(result, sympy.Basic):
342
+ result = complex(result)
343
+ return result
344
+
345
+ def norm(self) -> float:
346
+ """Returns the norm of the wavefunction."""
347
+ return np.sqrt(self.inner(self))
348
+
349
+ def normalize(self, inplace: bool = False) -> QubitWaveFunction:
165
350
  """
166
- Complex values like (x+iy)|...> will currently not work, you need to type Real and imaginary separately
167
- Or improve this constructor :-)
168
- e.g instead of (0.5+1.0j)|0101> do 0.5|0101> + 1.0j|0101>
169
- :param paths:
170
- :param string:
171
- :return:
351
+ Normalizes the wavefunction.
352
+
353
+ :param inplace: Whether to normalize the wavefunction in place or return a new one.
354
+ :return: The normalized wavefunction.
172
355
  """
173
- try:
174
- state = dict()
175
- string = string.replace(" ", "")
176
- string = string.replace("*", "")
177
- string = string.replace("+-", "-")
178
- string = string.replace("-+", "-")
179
- terms = (string + "terminate").split('>')
180
- for term in terms:
181
- if term == 'terminate':
182
- break
183
- tmp = term.split("|")
184
- coeff = tmp[0]
185
- if coeff == '':
186
- coeff = 1.0
187
- else:
188
- coeff = complex(coeff)
189
- basis_state = BitString.from_binary(binary=tmp[1])
356
+ norm = self.norm()
357
+ if inplace:
358
+ self *= 1.0 / norm
359
+ return self
360
+ else:
361
+ return (1.0 / norm) * QubitWaveFunction.from_wavefunction(self)
190
362
 
191
- state[basis_state] = coeff
192
- except ValueError:
193
- raise TequilaException("Failed to initialize QubitWaveFunction from string:" + string + "\n"
194
- "did you try complex values?\n"
195
- "currently you need to type real and imaginary parts separately\n"
196
- "e.g. instead of (0.5+1.0j)|0101> do 0.5|0101> + 1.0j|0101>")
197
- except:
198
- raise TequilaException("Failed to initialize QubitWaveFunction from string:" + string)
199
- return QubitWaveFunction(state=state, n_qubits=n_qubits)
363
+ # It would be nice to call this __len__, however for some reason this causes infinite loops
364
+ # when multiplying wave functions with some types of numbers from the right sight, likely
365
+ # because the __mul__ implementation of the number tries to perform some sort of array
366
+ # operation.
367
+ def length(self):
368
+ return sum(1 for (k, v) in self.raw_items() if abs(v) > 1e-6)
200
369
 
201
370
  def __repr__(self):
202
371
  result = str()
203
- for k, v in self.items():
204
- result += number_to_string(number=v) + "|" + str(k.binary) + "> "
205
- return result
206
-
207
- def __eq__(self, other):
208
- raise TequilaException("Wavefunction equality is not well-defined. Consider using inner"
209
- + " product equality, wf1.isclose(wf2).")
210
-
211
- def isclose(self : 'QubitWaveFunction',
212
- other : 'QubitWaveFunction',
213
- rtol : float=1e-5,
214
- atol : float=1e-8) -> bool:
215
- """Return whether this wavefunction is similar to the target wavefunction."""
216
- over1 = complex(self.inner(other))
217
- over2 = numpy.sqrt(complex(self.inner(self) * other.inner(other)))
218
- # Explicit casts to complex() is required if self or other are sympy
219
- # wavefunction with sympy-typed amplitudes
220
-
221
- # Check if the two numbers are equal.
222
- return numpy.isclose(over1, over2, rtol=rtol, atol=atol)
223
-
224
- def __add__(self, other):
225
- result = QubitWaveFunction(state=copy.deepcopy(self._state))
226
- for k, v in other.items():
227
- if k in result._state:
228
- result._state[k] += v
229
- else:
230
- result._state[k] = v
231
- return result
232
-
233
- def __sub__(self, other):
234
- return self + -1.0 * other
235
-
236
- def __iadd__(self, other):
237
- for k, v in other.items():
238
- if k in self._state:
239
- self._state[k] += v
372
+ for index, coeff in self.items():
373
+ index = index.integer
374
+ if self.numbering == BitNumbering.LSB:
375
+ index = reverse_int_bits(index, self._n_qubits)
376
+ if np.isclose(coeff.imag, 0.0):
377
+ result += f"{coeff.real:+2.4f} |{index:0{self._n_qubits}b}> "
240
378
  else:
241
- self._state[k] = v
242
- return self
243
-
244
- def __rmul__(self, other):
245
- result = QubitWaveFunction(state=copy.deepcopy(self._state))
246
- for k, v in result._state.items():
247
- result._state[k] *= other
248
- return result
249
-
250
- def inner(self, other):
251
- # currently very slow and not optimized in any way
252
- result = 0.0
253
- for k, v in self.items():
254
- if k in other._state:
255
- result += v.conjugate() * other._state[k]
379
+ result += f"({coeff.real:+2.4f} + {coeff.imag:+2.4f}i) |{index:0{self._n_qubits}b}> "
380
+ # If the wavefunction contains no states
381
+ if not result:
382
+ result = "empty wavefunction"
256
383
  return result
257
384
 
258
- def normalize(self):
259
- """
260
- NOT AN Inplace operation
261
- :return: Normalizes the wavefunction/countrate
262
- """
263
- norm2 = self.inner(other=self)
264
- normalized = 1.0 / numpy.sqrt(norm2) * self
265
- return normalized
266
-
267
- def compute_expectationvalue(self, operator: 'QubitHamiltonian') -> numbers.Real:
385
+ def compute_expectationvalue(self, operator: QubitHamiltonian) -> numbers.Real:
268
386
  tmp = self.apply_qubitoperator(operator=operator)
269
387
  E = self.inner(other=tmp)
270
- if hasattr(E, "imag") and numpy.isclose(E.imag, 0.0, atol=1.e-6):
388
+ if hasattr(E, "imag") and np.isclose(E.imag, 0.0, atol=1.e-6):
271
389
  return float(E.real)
272
390
  else:
273
391
  return E
274
392
 
275
- def apply_qubitoperator(self, operator: 'QubitHamiltonian'):
393
+ def apply_qubitoperator(self, operator: QubitHamiltonian) -> QubitWaveFunction:
276
394
  """
277
395
  Inefficient function which computes the action of a QubitHamiltonian on this wfn
278
396
  :param operator: QubitOperator
279
397
  :return: resulting Qubitwavefunction
280
398
  """
281
- result = QubitWaveFunction()
399
+ result = QubitWaveFunction(self.n_qubits, self._numbering)
282
400
  for ps in operator.paulistrings:
283
401
  result += self.apply_paulistring(paulistring=ps)
284
- result = result.simplify()
285
402
  return result
286
403
 
287
- def apply_paulistring(self, paulistring: 'PauliString'):
404
+ def apply_paulistring(self, paulistring: PauliString) -> QubitWaveFunction:
288
405
  """
289
406
  Inefficient function which computes action of a single paulistring
290
407
  :param paulistring: PauliString
291
408
  :return: Expectation Value
292
409
  """
293
- result = QubitWaveFunction()
410
+ result = QubitWaveFunction(self._n_qubits, self._numbering)
294
411
  for k, v in self.items():
295
412
  arr = k.array
296
413
  c = v
@@ -306,17 +423,3 @@ class QubitWaveFunction:
306
423
  raise TequilaException("unknown pauli: " + str(p))
307
424
  result[BitString.from_array(array=arr)] = c
308
425
  return paulistring.coeff * result
309
-
310
- def to_array(self):
311
- result = numpy.zeros(shape=2 ** self.n_qubits, dtype=complex)
312
- for k, v in self.items():
313
- result[int(k)] = v
314
- return result
315
-
316
- def simplify(self, threshold = 1.e-8):
317
- state = {}
318
- for k, v in self.state.items():
319
- if not numpy.isclose(v, 0.0, atol=threshold):
320
- state[k] = v
321
- return QubitWaveFunction(state=state)
322
-