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.
@@ -356,20 +356,20 @@ class BackendCircuitQibo(BackendCircuit):
356
356
  n_qubits = max(self.highest_qubit + 1, self.n_qubits, self.abstract_circuit.max_qubit() + 1)
357
357
  if initial_state is not None:
358
358
  if isinstance(initial_state, (int, np.int64)):
359
- wave = QubitWaveFunction.from_int(i=initial_state, n_qubits=n_qubits)
359
+ wave = QubitWaveFunction.from_basis_state(n_qubits, initial_state, self.numbering)
360
360
  elif isinstance(initial_state, str):
361
- wave = QubitWaveFunction.from_string(string=initial_state).to_array()
361
+ wave = QubitWaveFunction.from_string(initial_state, self.numbering).to_array()
362
362
  elif isinstance(initial_state, QubitWaveFunction):
363
363
  wave = initial_state
364
364
  elif isinstance(initial_state,np.ndarray):
365
- wave = QubitWaveFunction.from_array(initial_state)
365
+ wave = QubitWaveFunction.from_array(initial_state, self.numbering)
366
366
  else:
367
367
  raise TequilaQiboException('could not understand initial state of type {}'.format(type(initial_state)))
368
368
  state = wave.to_array()
369
369
  result = self.circuit(state)
370
370
  else:
371
371
  result = self.circuit()
372
- back= QubitWaveFunction.from_array(arr=result.numpy())
372
+ back= QubitWaveFunction.from_array(result.numpy(), self.numbering)
373
373
  return back
374
374
 
375
375
  def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
@@ -398,7 +398,7 @@ class BackendCircuitQibo(BackendCircuit):
398
398
  if isinstance(initial_state, BitString):
399
399
  initial_state = initial_state.integer
400
400
  if isinstance(initial_state, QubitWaveFunction):
401
- if len(initial_state.keys()) != 1:
401
+ if initial_state.length() != 1:
402
402
  return self.do_simulate(variables=variables,initial_state=initial_state, *args, **kwargs)
403
403
  initial_state = list(initial_state.keys())[0].integer
404
404
  if isinstance(initial_state,np.ndarray):
@@ -426,19 +426,18 @@ class BackendCircuitQibo(BackendCircuit):
426
426
  results transformed to tequila native QubitWaveFunction
427
427
  """
428
428
 
429
- result = QubitWaveFunction()
429
+ result = QubitWaveFunction(self.n_qubits, self.numbering)
430
430
  # todo there are faster ways
431
431
 
432
432
  for k, v in backend_result.frequencies(binary=True).items():
433
433
  converted_key = BitString.from_bitstring(other=BitString.from_binary(binary=k))
434
- result._state[converted_key] = v
435
-
434
+ result[converted_key] = v
436
435
 
437
436
  if target_qubits is not None:
438
437
  mapped_target = [self.qubit_map[q].number for q in target_qubits]
439
438
  mapped_full = [self.qubit_map[q].number for q in self.abstract_qubits]
440
439
  keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full)
441
- result = result.apply_keymap(keymap=keymap)
440
+ result = QubitWaveFunction.from_wavefunction(result, keymap, len(mapped_target))
442
441
 
443
442
  return result
444
443
 
@@ -521,21 +520,20 @@ class BackendCircuitQibo(BackendCircuit):
521
520
  n_qubits = max(self.highest_qubit + 1, self.n_qubits, self.abstract_circuit.max_qubit() + 1)
522
521
  if initial_state is not None:
523
522
  if isinstance(initial_state, int):
524
- wave=QubitWaveFunction.from_int(i=initial_state, n_qubits=n_qubits)
523
+ wave = QubitWaveFunction.from_basis_state(n_qubits, initial_state, self.numbering)
525
524
  elif isinstance(initial_state, str):
526
- wave = QubitWaveFunction.from_string(string=initial_state).to_array()
525
+ wave = QubitWaveFunction.from_string(initial_state, self.numbering).to_array()
527
526
  elif isinstance(initial_state, QubitWaveFunction):
528
527
  wave = initial_state
529
- elif isinstance(initial_state,np.ndarray):
530
- wave = QubitWaveFunction.from_array(arr=initial_state, n_qubits=n_qubits) # silly but necessary
528
+ elif isinstance(initial_state, np.ndarray):
529
+ wave = QubitWaveFunction.from_array(initial_state, self.numbering) # silly but necessary
531
530
  else:
532
531
  raise TequilaQiboException('received an unusable initial state of type {}'.format(type(initial_state)))
533
- state=wave.to_array()
534
- result = circuit(state,nshots=samples)
532
+ state = wave.to_array()
533
+ result = circuit(state, nshots=samples)
535
534
  else:
536
535
  result = circuit(nshots=samples)
537
536
 
538
-
539
537
  back = self.convert_measurements(backend_result=result)
540
538
  return back
541
539
 
@@ -1,22 +1,28 @@
1
1
  from tequila.simulators.simulator_base import BackendCircuit, QCircuit, BackendExpectationValue
2
+ from tequila.utils.bitstrings import reverse_int_bits
2
3
  from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
3
4
  from tequila import TequilaException, TequilaWarning
4
5
  from tequila import BitString, BitNumbering, BitStringLSB
5
6
  from tequila.utils.keymap import KeyMapRegisterToSubregister
6
7
  from tequila.utils import to_float
7
- import qiskit, numpy, warnings
8
+ from typing import Union
9
+ import warnings
10
+ import numpy as np
11
+ import qiskit, qiskit_aer, qiskit.providers.fake_provider
12
+ from qiskit import QuantumCircuit, transpile
8
13
 
9
- HAS_NOISE=True
14
+ HAS_NOISE = True
10
15
  try:
11
- from qiskit_aer import noise as qiskitnoise
16
+ from qiskit_aer import noise as qiskitnoise, AerSimulator
12
17
  except:
13
18
  HAS_NOISE = False
14
19
 
15
- HAS_IBMQ=True
20
+ HAS_IBMQ = True
16
21
  try:
17
- from qiskit.providers.ibmq import IBMQBackend
22
+ from qiskit_ibm_runtime import IBMBackend
18
23
  except:
19
- HAS_IBMQ=False
24
+ HAS_IBMQ = False
25
+
20
26
 
21
27
  def get_bit_flip(p):
22
28
  """
@@ -70,16 +76,24 @@ gate_qubit_lookup = {
70
76
  'multicontrol': 3
71
77
  }
72
78
 
73
- full_basis = ['x', 'y', 'z', 'id', 'u1', 'u2', 'u3', 'h','unitary','sx',
79
+ full_basis = ['x', 'y', 'z', 'id', 'u1', 'u2', 'u3', 'h', 'unitary', 'sx',
74
80
  'cx', 'cy', 'cz', 'cu3', 'ccx']
75
81
 
82
+
76
83
  def qiskit_device_dict():
77
- devices = {}
78
- devices.update({str(x).lower():x for x in qiskit.Aer.backends()})
79
- devices.update({str(x).lower():x for x in qiskit.test.mock.FakeProvider().backends()})
84
+ # As of Quiskit Aer 0.15.0, backends() also returns legacy backends which are not AerSimulators that will be
85
+ # deprecated in the future
86
+ # TODO: Instead of building a dict with all backends just to search it, search directly with `get_backend`
87
+ # https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.AerProvider.html#qiskit_aer.AerProvider.get_backend
88
+ devices = {backend.name.lower(): backend for backend in qiskit_aer.AerProvider().backends()
89
+ if isinstance(backend, AerSimulator)}
90
+
91
+ # FakeProvider has been removed, see https://github.com/Qiskit/qiskit/issues/10954
92
+ # devices.update({str(x).lower(): x for x in qiskit.test.mock.FakeProvider().backends()})
80
93
 
81
94
  return devices
82
95
 
96
+
83
97
  class TequilaQiskitException(TequilaException):
84
98
  def __str__(self):
85
99
  return "Error in qiskit backend:" + self.message
@@ -125,6 +139,8 @@ class BackendCircuitQiskit(BackendCircuit):
125
139
  transform a tequila NoiseModel into a qiskit noise model.
126
140
 
127
141
  """
142
+ STATEVECTOR_DEVICE_NAME = "aer_simulator_statevector"
143
+
128
144
  compiler_arguments = {
129
145
  "trotterized": True,
130
146
  "swap": False,
@@ -145,7 +161,10 @@ class BackendCircuitQiskit(BackendCircuit):
145
161
  }
146
162
 
147
163
  numbering = BitNumbering.LSB
148
-
164
+
165
+ supports_sampling_initialization = True
166
+ supports_generic_initialization = True
167
+
149
168
  def __init__(self, abstract_circuit: QCircuit, variables, qubit_map=None, noise=None,
150
169
  device=None, *args, **kwargs):
151
170
  """
@@ -188,8 +207,9 @@ class BackendCircuitQiskit(BackendCircuit):
188
207
  if qubit_map is None:
189
208
  qubit_map = {q: i for i, q in enumerate(abstract_circuit.qubits)}
190
209
  else:
191
- warnings.warn("reveived custom qubit_map = {}\n"
192
- "This is not fully integrated with qiskit and might result in unexpected behaviour".format(qubit_map), TequilaWarning)
210
+ warnings.warn(f"reveived custom qubit_map = {qubit_map}\n"
211
+ "This is not fully integrated with qiskit and might result in unexpected behaviour",
212
+ TequilaWarning)
193
213
 
194
214
  n_qubits = max(qubit_map.values()) + 1
195
215
 
@@ -201,7 +221,7 @@ class BackendCircuitQiskit(BackendCircuit):
201
221
 
202
222
  self.classical_map = self.make_classical_map(qubit_map=self.qubit_map)
203
223
 
204
- if noise != None:
224
+ if noise is not None:
205
225
  self.noise_lookup = {
206
226
  'phase damp': qiskitnoise.phase_damping_error,
207
227
  'amplitude damp': qiskitnoise.amplitude_damping_error,
@@ -211,12 +231,12 @@ class BackendCircuitQiskit(BackendCircuit):
211
231
  'depolarizing': qiskitnoise.depolarizing_error
212
232
  }
213
233
 
214
- if isinstance(noise, str): #string noise means "use the same noise as the device I tell you to get."
234
+ if isinstance(noise, str): # string noise means "use the same noise as the device I tell you to get."
215
235
  try:
216
236
  self.check_device(noise)
217
237
  self.noise_model = qiskitnoise.NoiseModel.from_backend(noise)
218
238
  except TequilaQiskitException:
219
- raise TequilaException("noise init from string requires that noise names a device. Got {}".format(noise))
239
+ raise TequilaException(f"noise init from string requires that noise names a device. Got {noise}")
220
240
 
221
241
  else:
222
242
  self.noise_model = self.noise_model_converter(noise)
@@ -235,7 +255,7 @@ class BackendCircuitQiskit(BackendCircuit):
235
255
  qubit_map = super().make_qubit_map(qubits=qubits)
236
256
  mapped_qubits = [q.number for q in qubit_map.values()]
237
257
  for k, v in qubit_map.items():
238
- qubit_map[k].instance = self.q [v.number]
258
+ qubit_map[k].instance = self.q[v.number]
239
259
 
240
260
  return qubit_map
241
261
 
@@ -247,6 +267,20 @@ class BackendCircuitQiskit(BackendCircuit):
247
267
 
248
268
  return classical_map
249
269
 
270
+ def add_state_init(self, circuit: QuantumCircuit, initial_state: Union[int, QubitWaveFunction]) -> QuantumCircuit:
271
+ if initial_state == 0:
272
+ return circuit
273
+
274
+ if isinstance(initial_state, QubitWaveFunction):
275
+ statevector = initial_state.to_array(self.numbering)
276
+ else:
277
+ statevector = np.zeros(2 ** self.n_qubits)
278
+ statevector[reverse_int_bits(initial_state, self.n_qubits)] = 1.0
279
+
280
+ init_circuit = qiskit.QuantumCircuit(self.q, self.c)
281
+ init_circuit.set_statevector(statevector)
282
+ return init_circuit.compose(circuit)
283
+
250
284
  def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
251
285
  """
252
286
  Helper function for performing simulation.
@@ -266,10 +300,12 @@ class BackendCircuitQiskit(BackendCircuit):
266
300
  """
267
301
  if self.noise_model is None:
268
302
  if self.device is None:
269
- qiskit_backend = self.retrieve_device('statevector_simulator')
303
+ qiskit_backend = self.retrieve_device(self.STATEVECTOR_DEVICE_NAME)
270
304
  else:
271
305
  if 'statevector' not in str(self.device):
272
- raise TequilaException('For simulation, only state vector simulators are supported; recieved device={}, you might have forgoten to set the samples keyword - e.g. (device={}, samples=1000). If not set, tequila assumes that full wavefunction simualtion is demanded which is not compatible with qiskit devices or fake devices except for device=statevector'.format(self.device, self.device))
306
+ raise TequilaException(
307
+ 'For simulation, only state vector simulators are supported; recieved device={}, you might have forgoten to set the samples keyword - e.g. (device={}, samples=1000). If not set, tequila assumes that full wavefunction simualtion is demanded which is not compatible with qiskit devices or fake devices except for device=statevector'.format(
308
+ self.device, self.device))
273
309
  else:
274
310
  qiskit_backend = self.retrieve_device(self.device)
275
311
  else:
@@ -279,22 +315,16 @@ class BackendCircuitQiskit(BackendCircuit):
279
315
  if "optimization_level" in kwargs:
280
316
  optimization_level = kwargs['optimization_level']
281
317
 
282
- opts = {}
283
- if initial_state != 0:
284
- array = numpy.zeros(shape=[2 ** self.n_qubits])
285
- i = BitStringLSB.from_binary(BitString.from_int(integer=initial_state, nbits=self.n_qubits).binary)
286
- print(initial_state, " -> ", i)
287
- array[i.integer] = 1.0
288
- opts = {"initial_statevector": array}
289
-
290
- circuit = self.circuit.bind_parameters(self.resolver)
318
+ circuit = self.circuit.assign_parameters(self.resolver)
319
+ circuit = self.add_state_init(circuit, initial_state)
320
+ circuit.save_statevector()
321
+ circuit = transpile(circuit, qiskit_backend)
322
+ backend_result = qiskit_backend.run(circuit, optimization_level=optimization_level).result()
291
323
 
292
- qiskit_job = qiskit_backend.run(circuit,optimization_level=optimization_level,**opts)
324
+ return QubitWaveFunction.from_array(array=backend_result.get_statevector(circuit).data, numbering=self.numbering)
293
325
 
294
- backend_result = qiskit_job.result()
295
- return QubitWaveFunction.from_array(arr=backend_result.get_statevector(circuit), numbering=self.numbering)
296
-
297
- def do_sample(self, circuit: qiskit.QuantumCircuit, samples: int, read_out_qubits, *args, **kwargs) -> QubitWaveFunction:
326
+ def do_sample(self, circuit: qiskit.QuantumCircuit, samples: int, read_out_qubits, initial_state=0, *args,
327
+ **kwargs) -> QubitWaveFunction:
298
328
  """
299
329
  Helper function for performing sampling.
300
330
  Parameters
@@ -303,6 +333,8 @@ class BackendCircuitQiskit(BackendCircuit):
303
333
  the circuit from which to sample.
304
334
  samples:
305
335
  the number of samples to take.
336
+ initial_state:
337
+ initial state of the circuit
306
338
  args
307
339
  kwargs
308
340
 
@@ -315,27 +347,28 @@ class BackendCircuitQiskit(BackendCircuit):
315
347
  if 'optimization_level' in kwargs:
316
348
  optimization_level = kwargs['optimization_level']
317
349
  if self.device is None:
318
- qiskit_backend = self.retrieve_device('aer_simulator')
350
+ qiskit_backend = self.retrieve_device(self.STATEVECTOR_DEVICE_NAME)
319
351
  else:
320
352
  qiskit_backend = self.retrieve_device(self.device)
321
353
 
322
- if isinstance(qiskit_backend,IBMQBackend):
354
+ if HAS_IBMQ and isinstance(qiskit_backend, IBMBackend):
323
355
  if self.noise_model is not None:
324
- raise TequilaException('Cannot combine backend {} with custom noise models.'.format(str(qiskit_backend)))
325
- circuit = circuit.bind_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
356
+ raise TequilaException(
357
+ 'Cannot combine backend {} with custom noise models.'.format(str(qiskit_backend)))
358
+ circuit = circuit.assign_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
326
359
  circuit = qiskit.transpile(circuit, qiskit_backend)
327
- return self.convert_measurements(qiskit_backend.run(circuit,shots=samples,
328
- optimization_level=optimization_level),
360
+ return self.convert_measurements(qiskit_backend.run(circuit, shots=samples,
361
+ optimization_level=optimization_level),
329
362
  target_qubits=read_out_qubits)
330
363
  else:
331
- if isinstance(qiskit_backend, qiskit.test.mock.FakeBackend):
332
- circuit = circuit.bind_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
364
+ if isinstance(qiskit_backend, qiskit.providers.fake_provider.FakeBackend):
365
+ circuit = circuit.assign_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
333
366
  coupling_map = qiskit_backend.configuration().coupling_map
334
367
  from_back = qiskitnoise.NoiseModel.from_backend(qiskit_backend)
335
368
  if self.noise_model is not None:
336
369
  from_back = self.noise_model
337
370
  basis = from_back.basis_gates
338
- use_backend = self.retrieve_device('aer_simulator')
371
+ use_backend = self.retrieve_device(self.STATEVECTOR_DEVICE_NAME)
339
372
  use_backend.set_options(noise_model=from_back)
340
373
  circuit = qiskit.transpile(circuit, backend=use_backend,
341
374
  basis_gates=basis,
@@ -343,23 +376,23 @@ class BackendCircuitQiskit(BackendCircuit):
343
376
  optimization_level=optimization_level
344
377
  )
345
378
 
346
- job=qiskit_backend.run(circuit, shots=samples)
347
- return self.convert_measurements(job,target_qubits=read_out_qubits)
379
+ job = qiskit_backend.run(circuit, shots=samples)
380
+ return self.convert_measurements(job, target_qubits=read_out_qubits)
348
381
  else:
349
382
  if self.noise_model is not None:
350
383
  qiskit_backend.set_options(noise_model=self.noise_model) # fits better with our methodology.
351
384
  use_basis = full_basis
352
385
  else:
353
386
  use_basis = qiskit_backend.configuration().basis_gates
354
- circuit = circuit.bind_parameters(self.resolver) # this is necessary -- see qiskit-aer issue 1346
387
+ circuit = circuit.assign_parameters(self.resolver) # this is necessary -- see qiskit-aer issue 1346
388
+ circuit = self.add_state_init(circuit, initial_state)
355
389
  circuit = qiskit.transpile(circuit, backend=qiskit_backend,
356
390
  basis_gates=use_basis,
357
391
  optimization_level=optimization_level
358
392
  )
359
393
 
360
394
  job = qiskit_backend.run(circuit, shots=samples)
361
- return self.convert_measurements(job,
362
- target_qubits=read_out_qubits)
395
+ return self.convert_measurements(job, target_qubits=read_out_qubits)
363
396
 
364
397
  def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveFunction:
365
398
  """
@@ -374,16 +407,17 @@ class BackendCircuitQiskit(BackendCircuit):
374
407
  measurements converted into wave function form.
375
408
  """
376
409
  qiskit_counts = backend_result.result().get_counts()
377
- result = QubitWaveFunction()
410
+ result = QubitWaveFunction(self.n_qubits, self.numbering)
378
411
  # todo there are faster ways
379
412
  for k, v in qiskit_counts.items():
380
- converted_key = BitString.from_bitstring(other=BitStringLSB.from_binary(binary=k))
381
- result._state[converted_key] = v
413
+ # Qiskit uses LSB bitstrings, but from_binary expects MSB
414
+ converted_key = BitString.from_binary(k[::-1])
415
+ result[converted_key] = v
382
416
  if target_qubits is not None:
383
417
  mapped_target = [self.qubit_map[q].number for q in target_qubits]
384
418
  mapped_full = [self.qubit_map[q].number for q in self.abstract_qubits]
385
419
  keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full)
386
- result = result.apply_keymap(keymap=keymap)
420
+ result = QubitWaveFunction.from_wavefunction(result, keymap, n_qubits=len(target_qubits))
387
421
 
388
422
  return result
389
423
 
@@ -436,7 +470,8 @@ class BackendCircuitQiskit(BackendCircuit):
436
470
  par = float(gate.parameter)
437
471
  if gate.is_controlled():
438
472
  if len(gate.control) > 2:
439
- raise TequilaQiskitException("multi-controls beyond 2 not yet supported for the qiskit backend. Gate was:\n{}".format(gate) )
473
+ raise TequilaQiskitException(
474
+ "multi-controls beyond 2 not yet supported for the qiskit backend. Gate was:\n{}".format(gate))
440
475
  ops[1](circuit)(par, self.qubit(gate.control[0]), self.qubit(gate.target[0]))
441
476
  else:
442
477
  ops[0](circuit)(par, self.qubit(gate.target[0]))
@@ -590,7 +625,7 @@ class BackendCircuitQiskit(BackendCircuit):
590
625
  if device is None:
591
626
  return
592
627
 
593
- elif isinstance(device,qiskit.providers.Backend):
628
+ elif isinstance(device, qiskit.providers.Backend):
594
629
  return
595
630
 
596
631
  elif isinstance(device, dict):
@@ -0,0 +1,9 @@
1
+ from tequila.simulators.simulator_qiskit import BackendCircuitQiskit, BackendExpectationValueQiskit
2
+
3
+
4
+ class BackendCircuitQiskitGpu(BackendCircuitQiskit):
5
+ STATEVECTOR_DEVICE_NAME = "aer_simulator_statevector_gpu"
6
+
7
+
8
+ class BackendExpectationValueQiskitGpu(BackendExpectationValueQiskit):
9
+ BackendCircuitType = BackendCircuitQiskitGpu
@@ -395,10 +395,10 @@ class BackendCircuitQLM(BackendCircuit):
395
395
  if MY_QLM:
396
396
  result = PyLinalg().submit(job)
397
397
  statevector = get_statevector(result)
398
- return QubitWaveFunction.from_array(arr=statevector, numbering=self.numbering)
398
+ return QubitWaveFunction.from_array(array=statevector, numbering=self.numbering)
399
399
 
400
400
  result = LinAlg().submit(job)
401
- return QubitWaveFunction.from_array(arr=result.statevector, numbering=self.numbering)
401
+ return QubitWaveFunction.from_array(array=result.statevector, numbering=self.numbering)
402
402
 
403
403
  def update_variables(self, variables):
404
404
  """
@@ -431,7 +431,7 @@ class BackendCircuitQLM(BackendCircuit):
431
431
  QubitWaveFunction:
432
432
  measurements converted into wave function form.
433
433
  """
434
- result = QubitWaveFunction()
434
+ result = QubitWaveFunction(self.n_qubits, self.numbering)
435
435
  shots = int(backend_result.meta_data["nbshots"])
436
436
  nbits = backend_result[0].qregs[0].length
437
437
  for sample in backend_result:
@@ -1,9 +1,11 @@
1
+ from typing import Union
2
+
1
3
  import qulacs
2
4
  import numbers, numpy
3
5
  import warnings
4
6
 
5
7
  from tequila import TequilaException, TequilaWarning
6
- from tequila.utils.bitstrings import BitNumbering, BitString, BitStringLSB
8
+ from tequila.utils.bitstrings import BitNumbering, BitString, BitStringLSB, reverse_int_bits
7
9
  from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
8
10
  from tequila.simulators.simulator_base import BackendCircuit, BackendExpectationValue, QCircuit, change_basis
9
11
  from tequila.utils.keymap import KeyMapRegisterToSubregister
@@ -63,6 +65,11 @@ class BackendCircuitQulacs(BackendCircuit):
63
65
 
64
66
  numbering = BitNumbering.LSB
65
67
 
68
+ quantum_state_class = qulacs.QuantumState
69
+
70
+ supports_sampling_initialization = True
71
+ supports_generic_initialization = True
72
+
66
73
  def __init__(self, abstract_circuit, noise=None, *args, **kwargs):
67
74
  """
68
75
 
@@ -110,10 +117,17 @@ class BackendCircuitQulacs(BackendCircuit):
110
117
 
111
118
  self.circuit=self.add_noise_to_circuit(noise)
112
119
 
113
- def initialize_state(self, n_qubits:int=None) -> qulacs.QuantumState:
120
+ def initialize_state(self, n_qubits: int = None, initial_state: Union[int, QubitWaveFunction] = None) -> qulacs.QuantumState:
114
121
  if n_qubits is None:
115
122
  n_qubits = self.n_qubits
116
- return qulacs.QuantumState(n_qubits)
123
+
124
+ state = self.quantum_state_class(n_qubits)
125
+ if isinstance(initial_state, int):
126
+ state.set_computational_basis(reverse_int_bits(initial_state, self.n_qubits))
127
+ elif isinstance(initial_state, QubitWaveFunction):
128
+ state.load(initial_state.to_array(self.numbering))
129
+
130
+ return state
117
131
 
118
132
  def update_variables(self, variables):
119
133
  """
@@ -130,7 +144,7 @@ class BackendCircuitQulacs(BackendCircuit):
130
144
  for k, angle in enumerate(self.variables):
131
145
  self.circuit.set_parameter(k, angle(variables))
132
146
 
133
- def do_simulate(self, variables, initial_state, *args, **kwargs):
147
+ def do_simulate(self, variables, initial_state: Union[int, QubitWaveFunction], *args, **kwargs):
134
148
  """
135
149
  Helper function to perform simulation.
136
150
 
@@ -148,12 +162,10 @@ class BackendCircuitQulacs(BackendCircuit):
148
162
  QubitWaveFunction:
149
163
  QubitWaveFunction representing result of the simulation.
150
164
  """
151
- state = self.initialize_state(self.n_qubits)
152
- lsb = BitStringLSB.from_int(initial_state, nbits=self.n_qubits)
153
- state.set_computational_basis(BitString.from_binary(lsb.binary).integer)
165
+ state = self.initialize_state(self.n_qubits, initial_state)
154
166
  self.circuit.update_quantum_state(state)
155
167
 
156
- wfn = QubitWaveFunction.from_array(arr=state.get_vector(), numbering=self.numbering)
168
+ wfn = QubitWaveFunction.from_array(array=state.get_vector(), numbering=self.numbering)
157
169
  return wfn
158
170
 
159
171
  def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveFunction:
@@ -170,22 +182,17 @@ class BackendCircuitQulacs(BackendCircuit):
170
182
  results transformed to tequila native QubitWaveFunction
171
183
  """
172
184
 
173
- result = QubitWaveFunction()
185
+ result = QubitWaveFunction(self.n_qubits, self.numbering)
174
186
  # todo there are faster ways
175
187
 
176
-
177
188
  for k in backend_result:
178
- converted_key = BitString.from_binary(BitStringLSB.from_int(integer=k, nbits=self.n_qubits).binary)
179
- if converted_key in result._state:
180
- result._state[converted_key] += 1
181
- else:
182
- result._state[converted_key] = 1
189
+ result[k] += 1
183
190
 
184
191
  if target_qubits is not None:
185
192
  mapped_target = [self.qubit_map[q].number for q in target_qubits]
186
193
  mapped_full = [self.qubit_map[q].number for q in self.abstract_qubits]
187
194
  keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full)
188
- result = result.apply_keymap(keymap=keymap)
195
+ result = QubitWaveFunction.from_wavefunction(result, keymap, n_qubits=len(target_qubits))
189
196
 
190
197
  return result
191
198
 
@@ -211,9 +218,7 @@ class BackendCircuitQulacs(BackendCircuit):
211
218
  QubitWaveFunction:
212
219
  the results of sampling, as a Qubit Wave Function.
213
220
  """
214
- state = self.initialize_state(self.n_qubits)
215
- lsb = BitStringLSB.from_int(initial_state, nbits=self.n_qubits)
216
- state.set_computational_basis(BitString.from_binary(lsb.binary).integer)
221
+ state = self.initialize_state(self.n_qubits, initial_state)
217
222
  circuit.update_quantum_state(state)
218
223
  sampled = state.sampling(samples)
219
224
  return self.convert_measurements(backend_result=sampled, target_qubits=self.measurements)
@@ -424,13 +429,15 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
424
429
  use_mapping = True
425
430
  BackendCircuitType = BackendCircuitQulacs
426
431
 
427
- def simulate(self, variables, *args, **kwargs) -> numpy.array:
432
+ def simulate(self, variables, initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs) -> numpy.array:
428
433
  """
429
434
  Perform simulation of this expectationvalue.
430
435
  Parameters
431
436
  ----------
432
437
  variables:
433
438
  variables, to be supplied to the underlying circuit.
439
+ initial_state: int or QubitWaveFunction:
440
+ the initial state of the circuit
434
441
  args
435
442
  kwargs
436
443
 
@@ -448,7 +455,7 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
448
455
  return numpy.asarray[self.H]
449
456
 
450
457
  self.U.update_variables(variables)
451
- state = self.U.initialize_state(self.n_qubits)
458
+ state = self.U.initialize_state(self.n_qubits, initial_state)
452
459
  self.U.circuit.update_quantum_state(state)
453
460
  result = []
454
461
  for H in self.H:
@@ -490,7 +497,7 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
490
497
  result.append(qulacs_H)
491
498
  return result
492
499
 
493
- def sample(self, variables, samples, *args, **kwargs) -> numpy.array:
500
+ def sample(self, variables, samples, initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs) -> numpy.array:
494
501
  """
495
502
  Sample this Expectation Value.
496
503
  Parameters
@@ -499,6 +506,8 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
499
506
  variables, to supply to the underlying circuit.
500
507
  samples: int:
501
508
  the number of samples to take.
509
+ initial_state: int or QubitWaveFunction:
510
+ the initial state of the circuit
502
511
  args
503
512
  kwargs
504
513
 
@@ -508,13 +517,13 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
508
517
  the result of sampling as a number.
509
518
  """
510
519
  self.update_variables(variables)
511
- state = self.U.initialize_state(self.n_qubits)
520
+ state = self.U.initialize_state(self.n_qubits, initial_state)
512
521
  self.U.circuit.update_quantum_state(state)
513
522
  result = []
514
- for H in self._reduced_hamiltonians: # those are the hamiltonians which where non-used qubits are already traced out
523
+ for H in self._reduced_hamiltonians: # those are the hamiltonians which where non-used qubits are already traced out
515
524
  E = 0.0
516
525
  if H.is_all_z() and not self.U.has_noise:
517
- E = super().sample(samples=samples, variables=variables, *args, **kwargs)
526
+ E = super().sample(samples=samples, variables=variables, initial_state=initial_state, *args, **kwargs)
518
527
  else:
519
528
  for ps in H.paulistrings:
520
529
  # change basis, measurement is destructive so the state will be copied
@@ -525,8 +534,8 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
525
534
  qbc = self.U.create_circuit(abstract_circuit=bc, variables=None)
526
535
  Esamples = []
527
536
  for sample in range(samples):
528
- if self.U.has_noise and sample>0:
529
- state = self.U.initialize_state(self.n_qubits)
537
+ if self.U.has_noise and sample > 0:
538
+ state = self.U.initialize_state(self.n_qubits, initial_state)
530
539
  self.U.circuit.update_quantum_state(state)
531
540
  state_tmp = state
532
541
  else:
@@ -535,7 +544,7 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
535
544
  qbc.update_quantum_state(state_tmp)
536
545
  ps_measure = 1.0
537
546
  for idx in ps.keys():
538
- assert idx in self.U.abstract_qubits # assert that the hamiltonian was really reduced
547
+ assert idx in self.U.abstract_qubits # assert that the hamiltonian was really reduced
539
548
  M = qulacs.gate.Measurement(self.U.qubit(idx), self.U.qubit(idx))
540
549
  M.update_quantum_state(state_tmp)
541
550
  measured = state_tmp.get_classical_value(self.U.qubit(idx))
@@ -1,16 +1,18 @@
1
1
  import qulacs
2
+ from qulacs_core import QuantumStateGpu
3
+
2
4
  from tequila import TequilaException
3
5
  from tequila.simulators.simulator_qulacs import BackendCircuitQulacs, BackendExpectationValueQulacs
4
6
 
7
+
5
8
  class TequilaQulacsGpuException(TequilaException):
6
9
  def __str__(self):
7
10
  return "Error in qulacs gpu backend:" + self.message
8
11
 
12
+
9
13
  class BackendCircuitQulacsGpu(BackendCircuitQulacs):
10
- def initialize_state(self, n_qubits:int=None) -> qulacs.QuantumState:
11
- if n_qubits is None:
12
- n_qubits = self.n_qubits
13
- return qulacs.QuantumStateGpu(n_qubits)
14
+ quantum_state_class = QuantumStateGpu
15
+
14
16
 
15
17
  class BackendExpectationValueQulacsGpu(BackendExpectationValueQulacs):
16
18
  BackendCircuitType = BackendCircuitQulacsGpu