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.
- tequila/apps/unary_state_prep.py +1 -2
- tequila/circuit/circuit.py +20 -15
- tequila/hamiltonian/paulis.py +4 -3
- tequila/objective/objective.py +5 -2
- tequila/quantumchemistry/chemistry_tools.py +8 -1
- tequila/quantumchemistry/encodings.py +2 -2
- tequila/quantumchemistry/orbital_optimizer.py +10 -2
- tequila/simulators/simulator_api.py +35 -9
- tequila/simulators/simulator_base.py +58 -28
- tequila/simulators/simulator_cirq.py +6 -6
- tequila/simulators/simulator_pyquil.py +3 -3
- tequila/simulators/simulator_qibo.py +14 -16
- tequila/simulators/simulator_qiskit.py +88 -53
- tequila/simulators/simulator_qiskit_gpu.py +9 -0
- tequila/simulators/simulator_qlm.py +3 -3
- tequila/simulators/simulator_qulacs.py +37 -28
- tequila/simulators/simulator_qulacs_gpu.py +6 -4
- tequila/simulators/simulator_spex.py +429 -0
- tequila/simulators/simulator_symbolic.py +15 -23
- tequila/simulators/test_spex_simulator.py +211 -0
- tequila/utils/bitstrings.py +14 -3
- tequila/version.py +1 -1
- tequila/wavefunction/qubit_wavefunction.py +340 -237
- {tequila_basic-1.9.7.dist-info → tequila_basic-1.9.9.dist-info}/METADATA +13 -9
- {tequila_basic-1.9.7.dist-info → tequila_basic-1.9.9.dist-info}/RECORD +28 -25
- {tequila_basic-1.9.7.dist-info → tequila_basic-1.9.9.dist-info}/WHEEL +1 -1
- {tequila_basic-1.9.7.dist-info → tequila_basic-1.9.9.dist-info/licenses}/LICENSE +0 -0
- {tequila_basic-1.9.7.dist-info → tequila_basic-1.9.9.dist-info}/top_level.txt +0 -0
@@ -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.
|
359
|
+
wave = QubitWaveFunction.from_basis_state(n_qubits, initial_state, self.numbering)
|
360
360
|
elif isinstance(initial_state, str):
|
361
|
-
wave = QubitWaveFunction.from_string(
|
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(
|
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
|
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
|
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 =
|
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.
|
523
|
+
wave = QubitWaveFunction.from_basis_state(n_qubits, initial_state, self.numbering)
|
525
524
|
elif isinstance(initial_state, str):
|
526
|
-
wave = QubitWaveFunction.from_string(
|
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(
|
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
|
-
|
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
|
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
|
-
|
78
|
-
|
79
|
-
|
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"
|
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
|
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):
|
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 {}"
|
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
|
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(
|
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(
|
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
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
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
|
-
|
324
|
+
return QubitWaveFunction.from_array(array=backend_result.get_statevector(circuit).data, numbering=self.numbering)
|
293
325
|
|
294
|
-
|
295
|
-
|
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(
|
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,
|
354
|
+
if HAS_IBMQ and isinstance(qiskit_backend, IBMBackend):
|
323
355
|
if self.noise_model is not None:
|
324
|
-
raise TequilaException(
|
325
|
-
|
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
|
-
|
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.
|
332
|
-
circuit = circuit.
|
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(
|
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.
|
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
|
-
|
381
|
-
|
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 =
|
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(
|
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(
|
398
|
+
return QubitWaveFunction.from_array(array=statevector, numbering=self.numbering)
|
399
399
|
|
400
400
|
result = LinAlg().submit(job)
|
401
|
-
return QubitWaveFunction.from_array(
|
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
|
-
|
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(
|
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
|
-
|
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 =
|
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:
|
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
|
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
|
-
|
11
|
-
|
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
|