Skip to content

Commit eb36f43

Browse files
authored
Merge pull request #372 from tequilahub/pr-1.9.8
merging to v.1.9.8
2 parents f416949 + 4ba944f commit eb36f43

27 files changed

+663
-447
lines changed

Diff for: .github/workflows/ci_backends.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
# myqlm does not work in python3.7
3333
pip install "cirq" "qiskit>=0.30" "qulacs" "qibo==0.1.1"
3434
elif [ $ver -eq 8 ]; then
35-
pip install "cirq" "qiskit>=0.30" "qulacs" "qibo==0.1.1" "myqlm" "cirq-google"
35+
pip install "cirq" "qiskit" "qulacs" "qibo==0.1.1" "myqlm" "cirq-google"
3636
fi
3737
pip install -e .
3838
- name: Lint with flake8

Diff for: README.md

+1-6
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,7 @@ You can avoid it by downgrading cirq and openfermion
342342
```bash
343343
pip install --upgrade "openfermion<=1.0.0"
344344
pip install --upgrade "cirq<=0.9.1"
345-
```
346-
347-
348-
## Qiskit backend
349-
Qiskit version 0.25 is not yet supported.
350-
`pip install --upgrade qiskit<0.25` fixes potential issues. If not: Please let us know.
345+
```
351346

352347
## Circuit drawing
353348
Standard graphical circuit representation within a Jupyter environment is often done using `tq.draw`.

Diff for: src/tequila/apps/unary_state_prep.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ def __init__(self, target_space: typing.List[BitString], max_repeat: int = 100,
8989
simulator.convert_to_numpy = False
9090
variables = None # {k:k.name.evalf() for k in self._abstract_circuit.extract_variables()}
9191
wfn = simulator.simulate(initial_state=BitString.from_int(0, nbits=self.n_qubits), variables=variables)
92-
wfn.n_qubits = self._n_qubits
9392
equations = []
9493
for k in target_space:
9594
equations.append(wfn[k] - abstract_coefficients[k])
@@ -174,7 +173,7 @@ def __call__(self, wfn: QubitWaveFunction) -> QCircuit:
174173
:return:
175174
"""
176175
try:
177-
assert (len(wfn) == len(self._target_space))
176+
assert wfn.length() == len(self._target_space)
178177
for key in wfn.keys():
179178
try:
180179
assert (key in self._target_space)

Diff for: src/tequila/hamiltonian/paulis.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def Projector(wfn, threshold=0.0, n_qubits=None) -> QubitHamiltonian:
256256
257257
"""
258258

259-
wfn = QubitWaveFunction(state=wfn, n_qubits=n_qubits)
259+
wfn = QubitWaveFunction.convert_from(n_qubits, wfn)
260260

261261
H = QubitHamiltonian.zero()
262262
for k1, v1 in wfn.items():
@@ -304,8 +304,9 @@ def KetBra(ket: QubitWaveFunction, bra: QubitWaveFunction, hermitian: bool = Fal
304304
305305
"""
306306
H = QubitHamiltonian.zero()
307-
ket = QubitWaveFunction(state=ket, n_qubits=n_qubits)
308-
bra = QubitWaveFunction(state=bra, n_qubits=n_qubits)
307+
308+
ket = QubitWaveFunction.convert_from(n_qubits, ket)
309+
bra = QubitWaveFunction.convert_from(n_qubits, bra)
309310

310311
for k1, v1 in bra.items():
311312
for k2, v2 in ket.items():

Diff for: src/tequila/quantumchemistry/encodings.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ def map_state(self, state: list, *args, **kwargs) -> list:
128128
fop = openfermion.FermionOperator(string, 1.0)
129129
op = self(fop)
130130
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
131-
wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits)
131+
wfn = QubitWaveFunction.from_basis_state(n_qubits, 0)
132132
wfn = wfn.apply_qubitoperator(operator=op)
133-
assert (len(wfn.keys()) == 1)
133+
assert wfn.length() == 1
134134
key = list(wfn.keys())[0].array
135135
return key
136136

Diff for: src/tequila/quantumchemistry/orbital_optimizer.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,16 @@ def kernel(self, h1, h2, *args, **kwargs):
208208
H = molecule.make_hardcore_boson_hamiltonian()
209209
else:
210210
H = molecule.make_hamiltonian()
211+
212+
rdm1 = None
213+
rdm2 = None
211214
if self.vqe_solver is not None:
212215
vqe_solver_arguments = {}
213216
if self.vqe_solver_arguments is not None:
214217
vqe_solver_arguments = self.vqe_solver_arguments
215218
result = self.vqe_solver(H=H, circuit=self.circuit, molecule=molecule, **vqe_solver_arguments)
219+
if hasattr(self.vqe_solver, "compute_rdms"):
220+
rdm1, rdm2 = self.vqe_solver.compute_rdms(U=self.circuit, variables=result.variables, molecule=molecule, use_hcb=restrict_to_hcb)
216221
elif self.circuit is None:
217222
raise Exception("Orbital Optimizer: Either provide a callable vqe_solver or a circuit")
218223
else:
@@ -233,8 +238,9 @@ def kernel(self, h1, h2, *args, **kwargs):
233238
# static ansatz
234239
U = self.circuit
235240

236-
rdm1, rdm2 = molecule.compute_rdms(U=U, variables=result.variables, spin_free=True, get_rdm1=True, get_rdm2=True, use_hcb=restrict_to_hcb)
237-
rdm2 = self.reorder(rdm2, 'dirac', 'mulliken')
241+
if rdm1 is None or rdm2 is None:
242+
rdm1, rdm2 = molecule.compute_rdms(U=U, variables=result.variables, spin_free=True, get_rdm1=True, get_rdm2=True, use_hcb=restrict_to_hcb)
243+
rdm2 = self.reorder(rdm2, 'dirac', 'mulliken')
238244
if not self.silent:
239245
print("{:20} : {}".format("energy", result.energy))
240246
if len(self.history) > 0:
@@ -259,3 +265,4 @@ def __str__(self):
259265
else:
260266
result += "{:30} : {}\n".format(k, v)
261267
return result
268+

Diff for: src/tequila/simulators/simulator_api.py

+25-9
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
from tequila.utils.exceptions import TequilaException, TequilaWarning
1010
from tequila.simulators.simulator_base import BackendCircuit, BackendExpectationValue
1111
from tequila.circuit.noise import NoiseModel
12+
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
1213

13-
SUPPORTED_BACKENDS = ["qulacs_gpu", "qulacs",'qibo', "qiskit", "cirq", "pyquil", "symbolic", "qlm"]
14-
SUPPORTED_NOISE_BACKENDS = ["qiskit", 'cirq', 'pyquil'] # qulacs removed in v.1.9
14+
SUPPORTED_BACKENDS = ["qulacs", "qulacs_gpu", "qibo", "qiskit", "qiskit_gpu", "cirq", "pyquil", "symbolic", "qlm"]
15+
SUPPORTED_NOISE_BACKENDS = ["qiskit", "qiskit_gpu", "cirq", "pyquil"] # qulacs removed in v.1.9
1516
BackendTypes = namedtuple('BackendTypes', 'CircType ExpValueType')
1617
INSTALLED_SIMULATORS = {}
1718
INSTALLED_SAMPLERS = {}
@@ -22,7 +23,6 @@
2223
from tequila.objective import Objective, Variable
2324
from tequila.circuit.gates import QCircuit
2425
import numbers.Real as RealNumber
25-
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
2626

2727
"""
2828
Check which simulators are installed
@@ -43,6 +43,19 @@
4343
HAS_QISKIT = False
4444
HAS_QISKIT_NOISE = False
4545

46+
try:
47+
pkg_resources.require("qiskit-aer-gpu")
48+
from tequila.simulators.simulator_qiskit_gpu import BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu
49+
HAS_QISKIT_GPU = True
50+
INSTALLED_SIMULATORS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
51+
INSTALLED_SAMPLERS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
52+
from tequila.simulators.simulator_qiskit import HAS_NOISE as HAS_QISKIT_GPU_NOISE
53+
if HAS_QISKIT_GPU_NOISE:
54+
INSTALLED_NOISE_SAMPLERS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
55+
except (ImportError, DistributionNotFound):
56+
HAS_QISKIT_GPU = False
57+
HAS_QISKIT_GPU_NOISE = False
58+
4659
HAS_QIBO = True
4760
try:
4861
from tequila.simulators.simulator_qibo import BackendCircuitQibo, BackendExpectationValueQibo
@@ -82,8 +95,8 @@
8295
HAS_QULACS = False
8396

8497
try:
85-
pkg_resources.require("qulacs-gpu")
86-
import qulacs
98+
# pkg_resources.require("qulacs-gpu")
99+
from qulacs import QuantumStateGpu
87100
from tequila.simulators.simulator_qulacs_gpu import BackendCircuitQulacsGpu, BackendExpectationValueQulacsGpu
88101

89102
HAS_QULACS_GPU = True
@@ -350,14 +363,15 @@ def compile_circuit(abstract_circuit: 'QCircuit',
350363
return CircType(abstract_circuit=abstract_circuit, variables=variables, noise=noise, device=device, *args, **kwargs)
351364

352365

353-
def simulate(objective: typing.Union['Objective', 'QCircuit','QTensor'],
366+
def simulate(objective: typing.Union['Objective', 'QCircuit', 'QTensor'],
354367
variables: Dict[Union[Variable, Hashable], RealNumber] = None,
355368
samples: int = None,
356369
backend: str = None,
357370
noise: NoiseModel = None,
358371
device: str = None,
372+
initial_state: Union[int, QubitWaveFunction] = 0,
359373
*args,
360-
**kwargs) -> Union[RealNumber, 'QubitWaveFunction']:
374+
**kwargs) -> Union[RealNumber, QubitWaveFunction]:
361375
"""Simulate a tequila objective or circuit
362376
363377
Parameters
@@ -375,6 +389,8 @@ def simulate(objective: typing.Union['Objective', 'QCircuit','QTensor'],
375389
specify a noise model to apply to simulation/sampling
376390
device:
377391
a device upon which (or in emulation of which) to sample
392+
initial_state: int or QubitWaveFunction:
393+
the initial state of the circuit
378394
*args :
379395
380396
**kwargs :
@@ -394,9 +410,9 @@ def simulate(objective: typing.Union['Objective', 'QCircuit','QTensor'],
394410
objective.extract_variables()))
395411

396412
compiled_objective = compile(objective=objective, samples=samples, variables=variables, backend=backend,
397-
noise=noise,device=device, *args, **kwargs)
413+
noise=noise, device=device, *args, **kwargs)
398414

399-
return compiled_objective(variables=variables, samples=samples, *args, **kwargs)
415+
return compiled_objective(variables=variables, samples=samples, initial_state=initial_state, *args, **kwargs)
400416

401417

402418
def draw(objective, variables=None, backend: str = None, name=None, *args, **kwargs):

Diff for: src/tequila/simulators/simulator_base.py

+35-12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from tequila import BitString
88
from tequila.objective.objective import Variable, format_variable_dictionary
99
from tequila.circuit import compiler
10+
from typing import Union
1011

1112
import numbers, typing, numpy, copy, warnings
1213

@@ -107,6 +108,11 @@ class BackendCircuit():
107108
"cc_max": True
108109
}
109110

111+
# Can be overwritten by backends that allow basis state initialization when sampling
112+
supports_sampling_initialization: bool = False
113+
# Can be overwritten by backends that allow initializing arbitrary states
114+
supports_generic_initialization: bool = False
115+
110116
@property
111117
def n_qubits(self) -> numbers.Integral:
112118
return len(self.qubit_map)
@@ -328,7 +334,7 @@ def update_variables(self, variables):
328334
"""
329335
self.circuit = self.create_circuit(abstract_circuit=self.abstract_circuit, variables=variables)
330336

331-
def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
337+
def simulate(self, variables, initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs) -> QubitWaveFunction:
332338
"""
333339
simulate the circuit via the backend.
334340
@@ -348,34 +354,43 @@ def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunc
348354
the wavefunction of the system produced by the action of the circuit on the initial state.
349355
350356
"""
357+
if isinstance(initial_state, QubitWaveFunction) and not self.supports_generic_initialization:
358+
raise TequilaException("Backend does not support arbitrary initial states")
359+
351360
self.update_variables(variables)
352361
if isinstance(initial_state, BitString):
353362
initial_state = initial_state.integer
354-
if isinstance(initial_state, QubitWaveFunction):
355-
if len(initial_state.keys()) != 1:
356-
raise TequilaException("only product states as initial states accepted")
357-
initial_state = list(initial_state.keys())[0].integer
358363

359364
all_qubits = list(range(self.abstract_circuit.n_qubits))
360365
active_qubits = self.qubit_map.keys()
361366

362367
# Keymap is only necessary if not all qubits are active
363368
keymap_required = sorted(active_qubits) != all_qubits
364369

370+
# Combining keymap and general initial states is awkward, because it's not clear what should happen with
371+
# different states on non-active qubits. For now, this is simply not allowed.
372+
# A better solution might be to check if all components of the initial state differ only on the active qubits.
373+
if keymap_required and isinstance(initial_state, QubitWaveFunction):
374+
raise TequilaException("Can only set non-basis initial state if all qubits are used")
375+
365376
if keymap_required:
366377
# maps from reduced register to full register
367378
keymap = KeyMapSubregisterToRegister(subregister=active_qubits, register=all_qubits)
368379

369-
mapped_initial_state = keymap.inverted(initial_state).integer if keymap_required else int(initial_state)
380+
if not isinstance(initial_state, QubitWaveFunction):
381+
mapped_initial_state = keymap.inverted(initial_state).integer if keymap_required else int(initial_state)
382+
else:
383+
mapped_initial_state = initial_state
384+
370385
result = self.do_simulate(variables=variables, initial_state=mapped_initial_state, *args,
371386
**kwargs)
372387

373388
if keymap_required:
374-
result.apply_keymap(keymap=keymap, initial_state=initial_state)
389+
result = QubitWaveFunction.from_wavefunction(result, keymap, n_qubits=len(all_qubits), initial_state=initial_state)
375390

376391
return result
377392

378-
def sample(self, variables, samples, read_out_qubits=None, circuit=None, *args, **kwargs):
393+
def sample(self, variables, samples, read_out_qubits=None, circuit=None, initial_state=0, *args, **kwargs):
379394
"""
380395
Sample the circuit. If circuit natively equips paulistrings, sample therefrom.
381396
Parameters
@@ -395,6 +410,12 @@ def sample(self, variables, samples, read_out_qubits=None, circuit=None, *args,
395410
The result of sampling, a recreated QubitWaveFunction in the sampled basis.
396411
397412
"""
413+
if initial_state != 0 and not self.supports_sampling_initialization:
414+
raise TequilaException("Backend does not support initial states for sampling")
415+
416+
if isinstance(initial_state, QubitWaveFunction) and not self.supports_generic_initialization:
417+
raise TequilaException("Backend does not support arbitrary initial states")
418+
398419
self.update_variables(variables)
399420
if read_out_qubits is None:
400421
read_out_qubits = self.abstract_qubits
@@ -406,7 +427,9 @@ def sample(self, variables, samples, read_out_qubits=None, circuit=None, *args,
406427
circuit = self.add_measurement(circuit=self.circuit, target_qubits=read_out_qubits)
407428
else:
408429
circuit = self.add_measurement(circuit=circuit, target_qubits=read_out_qubits)
409-
return self.do_sample(samples=samples, circuit=circuit, read_out_qubits=read_out_qubits, *args, **kwargs)
430+
431+
return self.do_sample(samples=samples, circuit=circuit, read_out_qubits=read_out_qubits,
432+
initial_state=initial_state, *args, **kwargs)
410433

411434
def sample_all_z_hamiltonian(self, samples: int, hamiltonian, variables, *args, **kwargs):
412435
"""
@@ -511,7 +534,7 @@ def sample_paulistring(self, samples: int, paulistring, variables, *args,
511534
E = E / samples * paulistring.coeff
512535
return E
513536

514-
def do_sample(self, samples, circuit, noise, abstract_qubits=None, *args, **kwargs) -> QubitWaveFunction:
537+
def do_sample(self, samples, circuit, noise, abstract_qubits=None, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
515538
"""
516539
helper function for sampling. MUST be overwritten by inheritors.
517540
@@ -777,7 +800,7 @@ def __call__(self, variables, samples: int = None, *args, **kwargs):
777800
raise TequilaException(
778801
"BackendExpectationValue received not all variables. Circuit depends on variables {}, you gave {}".format(
779802
self._variables, variables))
780-
803+
781804
if samples is None:
782805
data = self.simulate(variables=variables, *args, **kwargs)
783806
else:
@@ -856,7 +879,7 @@ def sample(self, variables, samples, *args, **kwargs) -> numpy.array:
856879
samples = max(1, int(self.abstract_expectationvalue.samples * total_samples))
857880
suggested = samples
858881
# samples are not necessarily set (either the user has to set it or some functions like optimize_measurements)
859-
882+
860883
if suggested is not None and suggested != samples:
861884
warnings.warn("simulating with samples={}, but expectationvalue carries suggested samples={}\nTry calling with samples='auto-total#ofsamples'".format(samples, suggested), TequilaWarning)
862885

Diff for: src/tequila/simulators/simulator_cirq.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveF
173173
simulator = cirq.Simulator()
174174
backend_result = simulator.simulate(program=self.circuit, param_resolver=self.resolver,
175175
initial_state=initial_state)
176-
return QubitWaveFunction.from_array(arr=backend_result.final_state_vector, numbering=self.numbering)
176+
return QubitWaveFunction.from_array(array=backend_result.final_state_vector, numbering=self.numbering)
177177

178178
def convert_measurements(self, backend_result: cirq.Result) -> QubitWaveFunction:
179179
"""
@@ -186,18 +186,18 @@ def convert_measurements(self, backend_result: cirq.Result) -> QubitWaveFunction
186186
Returns
187187
-------
188188
QubitWaveFunction:
189-
the result of sampling, as a tequila QubitWavefunction.
189+
the result of sampling, as a tequila QubitWaveFunction.
190190
191191
"""
192192
assert (len(backend_result.measurements) == 1)
193193
for key, value in backend_result.measurements.items():
194-
counter = QubitWaveFunction()
194+
counter = QubitWaveFunction(self.n_qubits, self.numbering)
195195
for sample in value:
196196
binary = BitString.from_array(array=sample.astype(int))
197-
if binary in counter._state:
198-
counter._state[binary] += 1
197+
if binary in counter.keys():
198+
counter[binary] += 1
199199
else:
200-
counter._state[binary] = 1
200+
counter[binary] = 1
201201
return counter
202202

203203
def do_sample(self, samples, circuit, *args, **kwargs) -> QubitWaveFunction:

Diff for: src/tequila/simulators/simulator_pyquil.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ def do_simulate(self, variables, initial_state, *args, **kwargs):
439439
if val > 0:
440440
iprep += pyquil.gates.X(i)
441441
backend_result = simulator.wavefunction(iprep + self.circuit, memory_map=self.resolver)
442-
return QubitWaveFunction.from_array(arr=backend_result.amplitudes, numbering=self.numbering)
442+
return QubitWaveFunction.from_array(array=backend_result.amplitudes, numbering=self.numbering)
443443

444444
def do_sample(self, samples, circuit, *args, **kwargs) -> QubitWaveFunction:
445445
"""
@@ -495,7 +495,7 @@ def string_to_array(s):
495495
listing.append(int(letter))
496496
return listing
497497

498-
result = QubitWaveFunction()
498+
result = QubitWaveFunction(self.n_qubits, self.numbering)
499499
bit_dict = {}
500500
for b in backend_result:
501501
try:
@@ -505,7 +505,7 @@ def string_to_array(s):
505505

506506
for k, v in bit_dict.items():
507507
arr = string_to_array(k)
508-
result._state[BitString.from_array(arr)] = v
508+
result[BitString.from_array(arr)] = v
509509
return result
510510

511511
def no_translation(self, abstract_circuit):

0 commit comments

Comments
 (0)