tequila-basic 1.9.5__py3-none-any.whl → 1.9.6__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/circuit/gates.py +1 -1
- tequila/circuit/qpic.py +1 -1
- tequila/grouping/binary_rep.py +1 -1
- tequila/quantumchemistry/chemistry_tools.py +122 -6
- tequila/quantumchemistry/encodings.py +141 -47
- tequila/quantumchemistry/madness_interface.py +4 -1
- tequila/quantumchemistry/qc_base.py +229 -11
- tequila/simulators/simulator_api.py +6 -1
- tequila/simulators/simulator_qiskit.py +14 -6
- tequila/tools/random_generators.py +17 -0
- tequila/version.py +1 -1
- {tequila_basic-1.9.5.dist-info → tequila_basic-1.9.6.dist-info}/METADATA +3 -9
- {tequila_basic-1.9.5.dist-info → tequila_basic-1.9.6.dist-info}/RECORD +16 -16
- {tequila_basic-1.9.5.dist-info → tequila_basic-1.9.6.dist-info}/WHEEL +1 -1
- {tequila_basic-1.9.5.dist-info → tequila_basic-1.9.6.dist-info}/LICENSE +0 -0
- {tequila_basic-1.9.5.dist-info → tequila_basic-1.9.6.dist-info}/top_level.txt +0 -0
tequila/circuit/gates.py
CHANGED
@@ -940,7 +940,7 @@ def QubitExcitation(angle: typing.Union[numbers.Real, Variable, typing.Hashable]
|
|
940
940
|
except:
|
941
941
|
raise Exception("QubitExcitation: Needs an even number of targets")
|
942
942
|
|
943
|
-
return QCircuit.wrap_gate(QubitExcitationImpl(angle=angle, target=target, assume_real=assume_real, compile_options=compile_options))
|
943
|
+
return QCircuit.wrap_gate(QubitExcitationImpl(angle=angle, target=target, assume_real=assume_real, compile_options=compile_options, control=control))
|
944
944
|
|
945
945
|
|
946
946
|
"""
|
tequila/circuit/qpic.py
CHANGED
@@ -224,7 +224,7 @@ def export_to(circuit,
|
|
224
224
|
'always_use_generators': True,
|
225
225
|
'group_together': "BARRIER"
|
226
226
|
}
|
227
|
-
elif not hasattr(
|
227
|
+
elif not hasattr(style, "items"):
|
228
228
|
raise Exception(
|
229
229
|
"style needs to be `tequila`, or `standard` or `generators` or a dictionary, you gave: {}".format(
|
230
230
|
str(style)))
|
tequila/grouping/binary_rep.py
CHANGED
@@ -26,7 +26,7 @@ class BinaryHamiltonian:
|
|
26
26
|
del Hof.terms[()]
|
27
27
|
hamiltonian = QubitHamiltonian.from_openfermion(Hof)
|
28
28
|
if n_qubits is None:
|
29
|
-
n_qubits = hamiltonian.
|
29
|
+
n_qubits = max(hamiltonian.qubits)+1
|
30
30
|
binary_terms = [
|
31
31
|
BinaryPauliString(
|
32
32
|
p.binary(n_qubits).binary,
|
@@ -2,12 +2,12 @@ import os
|
|
2
2
|
import typing
|
3
3
|
import warnings
|
4
4
|
from dataclasses import dataclass
|
5
|
-
|
5
|
+
from copy import deepcopy
|
6
|
+
from numbers import Real
|
6
7
|
import numpy
|
7
8
|
|
8
|
-
from tequila import BitString, QCircuit, TequilaException
|
9
|
+
from tequila import BitString, QCircuit, TequilaException,Variable,compile_circuit
|
9
10
|
from tequila.circuit import gates
|
10
|
-
|
11
11
|
try:
|
12
12
|
from openfermion.ops.representations import get_active_space_integrals # needs openfermion 1.3
|
13
13
|
except ImportError as E:
|
@@ -50,16 +50,132 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
50
50
|
self._name = "FermionicExcitation"
|
51
51
|
self.transformation = transformation
|
52
52
|
self.indices = indices
|
53
|
-
|
53
|
+
if not hasattr(indices[0],"__len__"):
|
54
|
+
self.indices = [(indices[2 * i], indices[2 * i+1]) for i in range(len(indices) // 2)]
|
55
|
+
self.sign = self.format_excitation_variables(self.indices)
|
56
|
+
self.indices = self.format_excitation_indices(self.indices)
|
54
57
|
def compile(self, *args, **kwargs):
|
55
58
|
if self.is_convertable_to_qubit_excitation():
|
56
59
|
target = []
|
57
60
|
for x in self.indices:
|
58
61
|
for y in x:
|
59
62
|
target.append(y)
|
60
|
-
return gates.QubitExcitation(target=target, angle
|
63
|
+
return gates.QubitExcitation(target=target, angle=self.parameter, control=self.control)
|
64
|
+
else:
|
65
|
+
if self.transformation.lower().strip("_") == "jordanwigner":
|
66
|
+
return self.fermionic_excitation(angle=self.sign*self.parameter, indices=self.indices, control=self.control,opt=False)
|
67
|
+
else:
|
68
|
+
return gates.Trotterized(generator=self.generator, control=self.control, angle=self.parameter, steps=1)
|
69
|
+
def format_excitation_indices(self, idx):
|
70
|
+
"""
|
71
|
+
Consistent formatting of excitation indices
|
72
|
+
idx = [(p0,q0),(p1,q1),...,(pn,qn)]
|
73
|
+
sorted as: p0<p1<pn and pi<qi
|
74
|
+
:param idx: list of index tuples describing a single(!) fermionic excitation
|
75
|
+
:return: list of index tuples
|
76
|
+
"""
|
77
|
+
|
78
|
+
idx = [tuple(sorted(x)) for x in idx]
|
79
|
+
idx = sorted(idx, key=lambda x: x[0])
|
80
|
+
return list(idx)
|
81
|
+
def format_excitation_variables(self, idx):
|
82
|
+
"""
|
83
|
+
Consistent formatting of excitation variable
|
84
|
+
idx = [(p0,q0),(p1,q1),...,(pn,qn)]
|
85
|
+
sorted as: pi<qi and p0 < p1 < p2
|
86
|
+
:param idx: list of index tuples describing a single(!) fermionic excitation
|
87
|
+
:return: sign of the variable with re-ordered indices
|
88
|
+
"""
|
89
|
+
sig = 1
|
90
|
+
for pair in idx:
|
91
|
+
if pair[1]>pair[0]:
|
92
|
+
sig *= -1
|
93
|
+
for pair in range(len(idx)-1):
|
94
|
+
if idx[pair+1][0]>idx[pair][0]:
|
95
|
+
sig *= -1
|
96
|
+
return sig
|
97
|
+
def cCRy(self, target: int, dcontrol: typing.Union[list, int], control: typing.Union[list, int],
|
98
|
+
angle: typing.Union[Real, Variable, typing.Hashable], case: int = 1) -> QCircuit:
|
99
|
+
'''
|
100
|
+
Compilation of CRy as on https://doi.org/10.1103/PhysRevA.102.062612
|
101
|
+
If not control passed, Ry returned
|
102
|
+
Parameters
|
103
|
+
----------
|
104
|
+
case: if 1 employs eq. 12 from the paper, if 0 eq. 13
|
105
|
+
'''
|
106
|
+
if control is not None and not len(control):
|
107
|
+
control = None
|
108
|
+
if isinstance(dcontrol, int):
|
109
|
+
dcontrol = [dcontrol]
|
110
|
+
if not len(dcontrol):
|
111
|
+
return compile_circuit(gates.Ry(angle=angle, target=target, control=control))
|
61
112
|
else:
|
62
|
-
|
113
|
+
if isinstance(angle, str):
|
114
|
+
angle = Variable(angle)
|
115
|
+
U = QCircuit()
|
116
|
+
aux = dcontrol[0]
|
117
|
+
ctr = deepcopy(dcontrol)
|
118
|
+
ctr.pop(0)
|
119
|
+
if case:
|
120
|
+
U += self.cCRy(target=target, dcontrol=ctr, angle=angle / 2, case=1, control=control) + gates.H(
|
121
|
+
aux) + gates.CNOT(target, aux)
|
122
|
+
U += self.cCRy(target=target, dcontrol=ctr, angle=-angle / 2, case=0, control=control) + gates.CNOT(
|
123
|
+
target, aux) + gates.H(aux)
|
124
|
+
else:
|
125
|
+
U += gates.H(aux) + gates.CNOT(target, aux) + self.cCRy(target=target, dcontrol=ctr, angle=-angle / 2,
|
126
|
+
case=0, control=control)
|
127
|
+
U += gates.CNOT(target, aux) + gates.H(aux) + self.cCRy(target=target, dcontrol=ctr, angle=angle / 2,
|
128
|
+
case=1, control=control)
|
129
|
+
return U
|
130
|
+
|
131
|
+
def fermionic_excitation(self, angle: typing.Union[Real, Variable, typing.Hashable], indices: typing.List,
|
132
|
+
control: typing.Union[int, typing.List] = None, opt: bool = True) -> QCircuit:
|
133
|
+
'''
|
134
|
+
Excitation [(i,j),(k,l)],... compiled following https://doi.org/10.1103/PhysRevA.102.062612
|
135
|
+
opt: whether to optimized CNOT H CNOT --> Rz Rz CNOT Rz
|
136
|
+
'''
|
137
|
+
lto = []
|
138
|
+
lfrom = []
|
139
|
+
if isinstance(indices,tuple) and not hasattr(indices[0],"__len__"):
|
140
|
+
indices = [(indices[2 * i], indices[2 * i + 1]) for i in range(len(indices) // 2)]
|
141
|
+
for pair in indices:
|
142
|
+
lfrom.append(pair[0])
|
143
|
+
lto.append(pair[1])
|
144
|
+
Upair = QCircuit()
|
145
|
+
if isinstance(angle, str) or isinstance(angle, tuple):
|
146
|
+
angle = Variable(angle)
|
147
|
+
for i in range(len(lfrom) - 1):
|
148
|
+
Upair += gates.CNOT(lfrom[i + 1], lfrom[i])
|
149
|
+
Upair += gates.CNOT(lto[i + 1], lto[i])
|
150
|
+
Upair += gates.X(lto[i]) + gates.X(lfrom[i])
|
151
|
+
Upair += gates.CNOT(lto[-1], lfrom[-1])
|
152
|
+
crt = lfrom[::-1] + lto
|
153
|
+
Uladder = QCircuit()
|
154
|
+
pairs = lfrom + lto
|
155
|
+
pairs.sort()
|
156
|
+
orbs = []
|
157
|
+
for o in range(len(pairs) // 2):
|
158
|
+
orbs += [*range(pairs[2 * o] + 1, pairs[2 * o + 1])]
|
159
|
+
if len(orbs):
|
160
|
+
for o in range(len(orbs) - 1):
|
161
|
+
Uladder += gates.CNOT(orbs[o], orbs[o + 1])
|
162
|
+
Uladder += compile_circuit(gates.CZ(orbs[-1], lto[-1]))
|
163
|
+
crt.pop(-1)
|
164
|
+
if control is not None and (isinstance(control, int) or len(control) == 1):
|
165
|
+
if isinstance(control, int):
|
166
|
+
crt.append(control)
|
167
|
+
else:
|
168
|
+
crt = crt + control
|
169
|
+
control = []
|
170
|
+
Ur = self.cCRy(target=lto[-1], dcontrol=crt, angle=angle, control=control)
|
171
|
+
Upair2 = Upair.dagger()
|
172
|
+
if opt:
|
173
|
+
Ur.gates.pop(-1)
|
174
|
+
Ur.gates.pop(-1)
|
175
|
+
Upair2.gates.pop(0)
|
176
|
+
Ur += gates.Rz(numpy.pi / 2, target=lto[-1]) + gates.Rz(-numpy.pi / 2, target=lfrom[-1])
|
177
|
+
Ur += gates.CNOT(lto[-1], lfrom[-1]) + gates.Rz(numpy.pi / 2, target=lfrom[-1]) + gates.H(lfrom[-1])
|
178
|
+
return Upair + Uladder + Ur + Uladder.dagger() + Upair2
|
63
179
|
|
64
180
|
def __str(self):
|
65
181
|
if self.indices is not None:
|
@@ -2,16 +2,21 @@
|
|
2
2
|
Collections of Fermion-to-Qubit encodings known to tequila
|
3
3
|
Most are Interfaces to OpenFermion
|
4
4
|
"""
|
5
|
+
import abc
|
6
|
+
|
7
|
+
from tequila import TequilaException
|
5
8
|
from tequila.circuit.circuit import QCircuit
|
6
|
-
from tequila.circuit.gates import X
|
9
|
+
from tequila.circuit.gates import X, CNOT
|
7
10
|
from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian
|
8
11
|
import openfermion
|
12
|
+
import numpy
|
13
|
+
|
9
14
|
|
10
15
|
def known_encodings():
|
11
16
|
# convenience for testing and I/O
|
12
|
-
encodings= {
|
13
|
-
"JordanWigner":JordanWigner,
|
14
|
-
"BravyiKitaev":BravyiKitaev,
|
17
|
+
encodings = {
|
18
|
+
"JordanWigner": JordanWigner,
|
19
|
+
"BravyiKitaev": BravyiKitaev,
|
15
20
|
"BravyiKitaevFast": BravyiKitaevFast,
|
16
21
|
"BravyiKitaevTree": BravyiKitaevTree,
|
17
22
|
"TaperedBravyiKitaev": TaperedBravyKitaev
|
@@ -22,14 +27,14 @@ def known_encodings():
|
|
22
27
|
"ReorderedBravyiKitaev": lambda **kwargs: BravyiKitaev(up_then_down=True, **kwargs),
|
23
28
|
"ReorderedBravyiKitaevTree": lambda **kwargs: BravyiKitaevTree(up_then_down=True, **kwargs),
|
24
29
|
}
|
25
|
-
return {k.replace("_","").replace("-","").upper():v for k,v in encodings.items()}
|
30
|
+
return {k.replace("_", "").replace("-", "").upper(): v for k, v in encodings.items()}
|
26
31
|
|
27
|
-
class EncodingBase:
|
28
32
|
|
33
|
+
class EncodingBase(metaclass=abc.ABCMeta):
|
29
34
|
# true if the encoding is fully integrated
|
30
35
|
# false: can only do special things (like building the Hamiltionian)
|
31
36
|
# but is not consistent with UCC gate generation
|
32
|
-
_ucc_support=False
|
37
|
+
_ucc_support = False
|
33
38
|
|
34
39
|
@property
|
35
40
|
def supports_ucc(self):
|
@@ -37,20 +42,20 @@ class EncodingBase:
|
|
37
42
|
|
38
43
|
@property
|
39
44
|
def name(self):
|
40
|
-
prefix=""
|
45
|
+
prefix = ""
|
41
46
|
if self.up_then_down:
|
42
|
-
prefix="Reordered"
|
47
|
+
prefix = "Reordered"
|
43
48
|
if hasattr(self, "_name"):
|
44
|
-
return prefix+self._name
|
49
|
+
return prefix + self._name
|
45
50
|
else:
|
46
|
-
return prefix+type(self).__name__
|
51
|
+
return prefix + type(self).__name__
|
47
52
|
|
48
53
|
def __init__(self, n_electrons, n_orbitals, up_then_down=False, *args, **kwargs):
|
49
54
|
self.n_electrons = n_electrons
|
50
55
|
self.n_orbitals = n_orbitals
|
51
56
|
self.up_then_down = up_then_down
|
52
57
|
|
53
|
-
def __call__(self, fermion_operator:openfermion.FermionOperator, *args, **kwargs) -> QubitHamiltonian:
|
58
|
+
def __call__(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> QubitHamiltonian:
|
54
59
|
"""
|
55
60
|
:param fermion_operator:
|
56
61
|
an openfermion FermionOperator
|
@@ -58,7 +63,8 @@ class EncodingBase:
|
|
58
63
|
The openfermion QubitOperator of this class ecoding
|
59
64
|
"""
|
60
65
|
if self.up_then_down:
|
61
|
-
op = openfermion.reorder(operator=fermion_operator, order_function=openfermion.up_then_down,
|
66
|
+
op = openfermion.reorder(operator=fermion_operator, order_function=openfermion.up_then_down,
|
67
|
+
num_modes=2 * self.n_orbitals)
|
62
68
|
else:
|
63
69
|
op = fermion_operator
|
64
70
|
|
@@ -73,18 +79,18 @@ class EncodingBase:
|
|
73
79
|
if self.up_then_down:
|
74
80
|
return i
|
75
81
|
else:
|
76
|
-
return 2*i
|
82
|
+
return 2 * i
|
77
83
|
|
78
84
|
def down(self, i):
|
79
85
|
if self.up_then_down:
|
80
|
-
return i+self.n_orbitals
|
86
|
+
return i + self.n_orbitals
|
81
87
|
else:
|
82
|
-
return 2*i+1
|
88
|
+
return 2 * i + 1
|
83
89
|
|
84
|
-
def do_transform(self, fermion_operator:openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
90
|
+
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
85
91
|
raise Exception("{}::do_transform: called base class".format(type(self).__name__))
|
86
92
|
|
87
|
-
def map_state(self, state:list, *args, **kwargs) -> list:
|
93
|
+
def map_state(self, state: list, *args, **kwargs) -> list:
|
88
94
|
"""
|
89
95
|
Expects a state in spin-orbital ordering
|
90
96
|
Returns the corresponding qubit state in the class encoding
|
@@ -112,7 +118,7 @@ class EncodingBase:
|
|
112
118
|
# default is a lazy workaround, but it workds
|
113
119
|
n_qubits = 2 * self.n_orbitals
|
114
120
|
|
115
|
-
spin_orbitals = sorted([i for i,x in enumerate(state) if int(x)==1])
|
121
|
+
spin_orbitals = sorted([i for i, x in enumerate(state) if int(x) == 1])
|
116
122
|
|
117
123
|
string = "1.0 ["
|
118
124
|
for i in spin_orbitals:
|
@@ -128,26 +134,82 @@ class EncodingBase:
|
|
128
134
|
key = list(wfn.keys())[0].array
|
129
135
|
return key
|
130
136
|
|
131
|
-
|
132
|
-
|
137
|
+
@abc.abstractmethod
|
138
|
+
def me_to_jw(self) -> QCircuit:
|
139
|
+
"""
|
140
|
+
This method needs to be implemented to enable default conversions via Jordan-Wigner
|
141
|
+
"""
|
142
|
+
pass
|
143
|
+
|
144
|
+
# independent conversion methods, these are used for default conversions
|
145
|
+
# arXiv:1808.10402 IV. B. 2, Eq. 57
|
146
|
+
# original: https://doi.org/10.1063/1.4768229
|
147
|
+
def _jw_to_bk(self) -> QCircuit:
|
148
|
+
U = QCircuit() # Constructs empty circuit
|
149
|
+
|
150
|
+
flipper = False
|
151
|
+
for i in range(self.n_orbitals * 2):
|
152
|
+
# even qubits only hold their own value
|
153
|
+
if i % 2 == 0:
|
154
|
+
continue
|
155
|
+
|
156
|
+
# sum always includes the last qubit
|
157
|
+
U += CNOT(control=i - 1, target=i)
|
158
|
+
|
159
|
+
# every second odd qubit ties together with the last odd qubit
|
160
|
+
if flipper:
|
161
|
+
U += CNOT(control=i - 2, target=i)
|
162
|
+
|
163
|
+
flipper = not flipper
|
164
|
+
|
165
|
+
# we have now created the 4x4 blocks on the diagonal of this operators matrix
|
166
|
+
|
167
|
+
# every power of 2 connects to the last power of 2
|
168
|
+
# this corresponds to the last row in the recursive definitions being all 1s
|
169
|
+
x = numpy.log2(i + 1)
|
170
|
+
if x.is_integer() and x >= 3:
|
171
|
+
x = int(x)
|
172
|
+
U += CNOT(control=2 ** (x - 1) - 1, target=i)
|
173
|
+
|
174
|
+
return U
|
175
|
+
|
176
|
+
def _hcb_to_jw(self):
|
177
|
+
U = QCircuit()
|
178
|
+
for i in range(self.n_orbitals):
|
179
|
+
U += X(target=self.down(i), control=self.up(i))
|
180
|
+
return U
|
181
|
+
|
182
|
+
# Convenience Methods
|
183
|
+
def jw_to_me(self) -> QCircuit:
|
184
|
+
return self.me_to_jw().dagger()
|
185
|
+
|
186
|
+
def me_to_bk(self) -> QCircuit:
|
187
|
+
return self.me_to_jw() + self._jw_to_bk()
|
188
|
+
|
189
|
+
def bk_to_me(self) -> QCircuit:
|
190
|
+
return self.me_to_bk().dagger()
|
191
|
+
|
192
|
+
def hcb_to_me(self) -> QCircuit:
|
193
|
+
return self._hcb_to_jw() + self.jw_to_me()
|
133
194
|
|
134
195
|
def __str__(self):
|
135
196
|
return type(self).__name__
|
136
197
|
|
198
|
+
|
137
199
|
class JordanWigner(EncodingBase):
|
138
200
|
"""
|
139
201
|
OpenFermion::jordan_wigner
|
140
202
|
"""
|
141
|
-
_ucc_support=True
|
203
|
+
_ucc_support = True
|
142
204
|
|
143
|
-
def do_transform(self, fermion_operator:openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
205
|
+
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
144
206
|
return openfermion.jordan_wigner(fermion_operator, *args, **kwargs)
|
145
207
|
|
146
|
-
def map_state(self, state:list, *args, **kwargs):
|
147
|
-
state = state + [0]*(self.n_orbitals-len(state))
|
148
|
-
result = [0]*len(state)
|
208
|
+
def map_state(self, state: list, *args, **kwargs):
|
209
|
+
state = state + [0] * (self.n_orbitals - len(state))
|
210
|
+
result = [0] * len(state)
|
149
211
|
if self.up_then_down:
|
150
|
-
return [state[2*i] for i in range(self.n_orbitals)] + [state[2*i+1] for i in range(self.n_orbitals)]
|
212
|
+
return [state[2 * i] for i in range(self.n_orbitals)] + [state[2 * i + 1] for i in range(self.n_orbitals)]
|
151
213
|
else:
|
152
214
|
return state
|
153
215
|
|
@@ -157,15 +219,34 @@ class JordanWigner(EncodingBase):
|
|
157
219
|
U += X(target=self.down(i), control=self.up(i))
|
158
220
|
return U
|
159
221
|
|
222
|
+
def me_to_jw(self) -> QCircuit:
|
223
|
+
return QCircuit()
|
224
|
+
|
225
|
+
def jw_to_me(self) -> QCircuit:
|
226
|
+
return QCircuit()
|
227
|
+
|
228
|
+
|
160
229
|
class BravyiKitaev(EncodingBase):
|
161
230
|
"""
|
162
231
|
Uses OpenFermion::bravyi_kitaev
|
163
232
|
"""
|
164
233
|
|
165
|
-
_ucc_support=True
|
234
|
+
_ucc_support = True
|
235
|
+
|
236
|
+
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
237
|
+
return openfermion.bravyi_kitaev(fermion_operator, n_qubits=self.n_orbitals * 2)
|
238
|
+
|
239
|
+
def me_to_jw(self) -> QCircuit:
|
240
|
+
return self._jw_to_bk().dagger()
|
166
241
|
|
167
|
-
def
|
168
|
-
return
|
242
|
+
def jw_to_me(self) -> QCircuit:
|
243
|
+
return self._jw_to_bk()
|
244
|
+
|
245
|
+
def bk_to_me(self) -> QCircuit:
|
246
|
+
return QCircuit()
|
247
|
+
|
248
|
+
def me_to_bk(self) -> QCircuit:
|
249
|
+
return QCircuit()
|
169
250
|
|
170
251
|
|
171
252
|
class BravyiKitaevTree(EncodingBase):
|
@@ -173,28 +254,37 @@ class BravyiKitaevTree(EncodingBase):
|
|
173
254
|
Uses OpenFermion::bravyi_kitaev_tree
|
174
255
|
"""
|
175
256
|
|
176
|
-
_ucc_support=True
|
257
|
+
_ucc_support = True
|
258
|
+
|
259
|
+
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
260
|
+
return openfermion.bravyi_kitaev_tree(fermion_operator, n_qubits=self.n_orbitals * 2)
|
261
|
+
|
262
|
+
def me_to_jw(self) -> QCircuit:
|
263
|
+
raise TequilaException("{}::me_to_jw: unimplemented".format(type(self).__name__))
|
177
264
|
|
178
|
-
def do_transform(self, fermion_operator:openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
179
|
-
return openfermion.bravyi_kitaev_tree(fermion_operator, n_qubits=self.n_orbitals*2)
|
180
265
|
|
181
266
|
class BravyiKitaevFast(EncodingBase):
|
182
267
|
"""
|
183
268
|
Uses OpenFermion::bravyi_kitaev_tree
|
184
269
|
"""
|
185
270
|
|
186
|
-
_ucc_support=False
|
271
|
+
_ucc_support = False
|
187
272
|
|
188
|
-
def do_transform(self, fermion_operator:openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
273
|
+
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
189
274
|
n_qubits = openfermion.count_qubits(fermion_operator)
|
190
|
-
if n_qubits != self.n_orbitals*2:
|
191
|
-
raise Exception(
|
275
|
+
if n_qubits != self.n_orbitals * 2:
|
276
|
+
raise Exception(
|
277
|
+
"BravyiKitaevFast transformation currently only possible for full Hamiltonians (no UCC generators).\nfermion_operator was {}".format(
|
278
|
+
fermion_operator))
|
192
279
|
op = openfermion.get_interaction_operator(fermion_operator)
|
193
280
|
return openfermion.bravyi_kitaev_fast(op)
|
194
281
|
|
195
|
-
|
282
|
+
def me_to_jw(self) -> QCircuit:
|
283
|
+
raise TequilaException("{}::me_to_jw: unimplemented".format(type(self).__name__))
|
196
284
|
|
197
|
-
|
285
|
+
|
286
|
+
class TaperedBravyKitaev(EncodingBase):
|
287
|
+
_ucc_support = False
|
198
288
|
|
199
289
|
"""
|
200
290
|
Uses OpenFermion::symmetry_conserving_bravyi_kitaev (tapered bravyi_kitaev_tree arxiv:1701.07072)
|
@@ -202,6 +292,7 @@ class TaperedBravyKitaev(EncodingBase):
|
|
202
292
|
See OpenFermion Documentation for more
|
203
293
|
Does not work for UCC generators yet
|
204
294
|
"""
|
295
|
+
|
205
296
|
def __init__(self, n_electrons, n_orbitals, active_fermions=None, active_orbitals=None, *args, **kwargs):
|
206
297
|
if active_fermions is None:
|
207
298
|
self.active_fermions = n_electrons
|
@@ -209,7 +300,7 @@ class TaperedBravyKitaev(EncodingBase):
|
|
209
300
|
self.active_fermions = active_fermions
|
210
301
|
|
211
302
|
if active_orbitals is None:
|
212
|
-
self.active_orbitals = n_orbitals*2
|
303
|
+
self.active_orbitals = n_orbitals * 2 # in openfermion those are spin-orbitals
|
213
304
|
else:
|
214
305
|
self.active_orbitals = active_orbitals
|
215
306
|
|
@@ -217,17 +308,20 @@ class TaperedBravyKitaev(EncodingBase):
|
|
217
308
|
raise Exception("Don't pass up_then_down argument to {}, it can't be changed".format(type(self).__name__))
|
218
309
|
super().__init__(n_orbitals=n_orbitals, n_electrons=n_electrons, up_then_down=False, *args, **kwargs)
|
219
310
|
|
220
|
-
def do_transform(self, fermion_operator:openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
221
|
-
if openfermion.count_qubits(fermion_operator) != self.n_orbitals*2:
|
311
|
+
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator:
|
312
|
+
if openfermion.count_qubits(fermion_operator) != self.n_orbitals * 2:
|
222
313
|
raise Exception("TaperedBravyiKitaev not ready for UCC generators yet")
|
223
|
-
return openfermion.symmetry_conserving_bravyi_kitaev(fermion_operator, active_orbitals=self.active_orbitals,
|
314
|
+
return openfermion.symmetry_conserving_bravyi_kitaev(fermion_operator, active_orbitals=self.active_orbitals,
|
315
|
+
active_fermions=self.active_fermions)
|
224
316
|
|
225
|
-
def map_state(self, state:list, *args, **kwargs):
|
226
|
-
non_tapered_trafo = BravyiKitaevTree(up_then_down=True, n_electrons=self.n_electrons,
|
317
|
+
def map_state(self, state: list, *args, **kwargs):
|
318
|
+
non_tapered_trafo = BravyiKitaevTree(up_then_down=True, n_electrons=self.n_electrons,
|
319
|
+
n_orbitals=self.n_orbitals)
|
227
320
|
key = non_tapered_trafo.map_state(state=state, *args, **kwargs)
|
228
|
-
n_qubits = self.n_orbitals*2
|
321
|
+
n_qubits = self.n_orbitals * 2
|
229
322
|
active_qubits = [i for i in range(n_qubits) if i not in [n_qubits - 1, n_qubits // 2 - 1]]
|
230
323
|
key = [key[i] for i in active_qubits]
|
231
324
|
return key
|
232
325
|
|
233
|
-
|
326
|
+
def me_to_jw(self) -> QCircuit:
|
327
|
+
raise TequilaException("{}::me_to_jw: unimplemented".format(type(self).__name__))
|
@@ -420,7 +420,10 @@ class QuantumChemistryMadness(QuantumChemistryBase):
|
|
420
420
|
|
421
421
|
"""
|
422
422
|
# check if the used qubit encoding has a hcb transformation
|
423
|
-
|
423
|
+
try:
|
424
|
+
have_hcb_trafo = self.transformation.hcb_to_me() is not None
|
425
|
+
except:
|
426
|
+
have_hcb_trafo = False
|
424
427
|
name = name.upper()
|
425
428
|
|
426
429
|
# Default Method
|
@@ -17,7 +17,7 @@ from .encodings import known_encodings
|
|
17
17
|
|
18
18
|
import typing, numpy, numbers
|
19
19
|
from itertools import product
|
20
|
-
|
20
|
+
import tequila.grouping.fermionic_functions as ff
|
21
21
|
|
22
22
|
|
23
23
|
try:
|
@@ -32,8 +32,7 @@ except:
|
|
32
32
|
except Exception as E:
|
33
33
|
raise Exception("{}\nIssue with Tequila Chemistry: Please update openfermion".format(str(E)))
|
34
34
|
import warnings
|
35
|
-
|
36
|
-
|
35
|
+
OPTIMIZED_ORDERING = "Optimized"
|
37
36
|
class QuantumChemistryBase:
|
38
37
|
"""
|
39
38
|
Base Class for tequila chemistry functionality
|
@@ -113,7 +112,11 @@ class QuantumChemistryBase:
|
|
113
112
|
h1 = molecule.integral_manager.one_body_integrals
|
114
113
|
h2 = molecule.integral_manager.two_body_integrals
|
115
114
|
S = molecule.integral_manager.overlap_integrals
|
116
|
-
active_orbitals
|
115
|
+
if "active_orbitals" not in kwargs:
|
116
|
+
active_orbitals = [o.idx_total for o in molecule.integral_manager.active_orbitals]
|
117
|
+
else:
|
118
|
+
active_orbitals = kwargs["active_orbitals"]
|
119
|
+
kwargs.pop("active_orbitals")
|
117
120
|
if transformation is None:
|
118
121
|
transformation = molecule.transformation
|
119
122
|
parameters = molecule.parameters
|
@@ -125,7 +128,8 @@ class QuantumChemistryBase:
|
|
125
128
|
active_orbitals= active_orbitals,
|
126
129
|
transformation=transformation,
|
127
130
|
orbital_type=molecule.integral_manager._orbital_type,
|
128
|
-
parameters=parameters,
|
131
|
+
parameters=parameters,
|
132
|
+
reference_orbitals= molecule.integral_manager.active_space.reference_orbitals,*args, **kwargs)
|
129
133
|
|
130
134
|
def supports_ucc(self):
|
131
135
|
"""
|
@@ -441,10 +445,14 @@ class QuantumChemistryBase:
|
|
441
445
|
|
442
446
|
generator = self.make_excitation_generator(indices=indices, remove_constant_term=control is None)
|
443
447
|
p0 = self.make_excitation_generator(indices=indices, form="P0", remove_constant_term=control is None)
|
444
|
-
|
448
|
+
if self.transformation.up_then_down:
|
449
|
+
idx = []
|
450
|
+
for pair in indices:
|
451
|
+
idx.append((pair[0]//2+(pair[0]%2)*self.n_orbitals,pair[1]//2+(pair[1]%2)*self.n_orbitals))
|
452
|
+
else:idx = indices
|
445
453
|
return QCircuit.wrap_gate(
|
446
454
|
FermionicGateImpl(angle=angle, generator=generator, p0=p0,
|
447
|
-
transformation=type(self.transformation).__name__.lower(), indices=
|
455
|
+
transformation=type(self.transformation).__name__.lower(), indices=idx,
|
448
456
|
assume_real=assume_real,
|
449
457
|
control=control, **kwargs))
|
450
458
|
|
@@ -1239,7 +1247,13 @@ class QuantumChemistryBase:
|
|
1239
1247
|
indices = self.make_upccgsd_indices(key=name)
|
1240
1248
|
|
1241
1249
|
# check if the used qubit encoding has a hcb transformation
|
1242
|
-
have_hcb_trafo =
|
1250
|
+
have_hcb_trafo = True
|
1251
|
+
try:
|
1252
|
+
if self.transformation.hcb_to_me() is None:
|
1253
|
+
have_hcb_trafo = False
|
1254
|
+
except:
|
1255
|
+
have_hcb_trafo = False
|
1256
|
+
|
1243
1257
|
|
1244
1258
|
# consistency checks for optimization
|
1245
1259
|
if have_hcb_trafo and hcb_optimization is None and include_reference:
|
@@ -1487,7 +1501,8 @@ class QuantumChemistryBase:
|
|
1487
1501
|
factor = 1.0 / trotter_steps
|
1488
1502
|
for step in range(trotter_steps):
|
1489
1503
|
for idx, angle in indices.items():
|
1490
|
-
|
1504
|
+
converted = [(idx[2 * i], idx[2 * i + 1]) for i in range(len(idx) // 2)]
|
1505
|
+
UCCSD += self.make_excitation_gate(indices=converted, angle=factor * angle)
|
1491
1506
|
if hasattr(initial_amplitudes,
|
1492
1507
|
"lower") and initial_amplitudes.lower() == "mp2" and parametrized and add_singles:
|
1493
1508
|
# mp2 has no singles, need to initialize them here (if not parametrized initializling as 0.0 makes no sense though)
|
@@ -1721,7 +1736,8 @@ class QuantumChemistryBase:
|
|
1721
1736
|
return None
|
1722
1737
|
|
1723
1738
|
def compute_rdms(self, U: QCircuit = None, variables: Variables = None, spin_free: bool = True,
|
1724
|
-
get_rdm1: bool = True, get_rdm2: bool = True, ordering="dirac", use_hcb: bool = False
|
1739
|
+
get_rdm1: bool = True, get_rdm2: bool = True, ordering="dirac", use_hcb: bool = False,
|
1740
|
+
rdm_trafo: QubitHamiltonian = None, decompose=None):
|
1725
1741
|
"""
|
1726
1742
|
Computes the one- and two-particle reduced density matrices (rdm1 and rdm2) given
|
1727
1743
|
a unitary U. This method uses the standard ordering in physics as denoted below.
|
@@ -1749,6 +1765,9 @@ class QuantumChemistryBase:
|
|
1749
1765
|
get_rdm1, get_rdm2 :
|
1750
1766
|
Set whether either one or both rdm1, rdm2 should be computed. If both are needed at some point,
|
1751
1767
|
it is recommended to compute them at once.
|
1768
|
+
rdm_trafo :
|
1769
|
+
The rdm operators can be transformed, e.g., a^dagger_i a_j -> U^dagger a^dagger_i a_j U,
|
1770
|
+
where U represents the transformation. The default is set to None, implying that U equas the identity.
|
1752
1771
|
|
1753
1772
|
Returns
|
1754
1773
|
-------
|
@@ -1993,8 +2012,18 @@ class QuantumChemistryBase:
|
|
1993
2012
|
# Transform operator lists to QubitHamiltonians
|
1994
2013
|
if (not use_hcb):
|
1995
2014
|
qops = [_get_qop_hermitian(op) for op in qops]
|
2015
|
+
|
1996
2016
|
# Compute expected values
|
1997
|
-
|
2017
|
+
if rdm_trafo is None:
|
2018
|
+
if decompose is not None:
|
2019
|
+
print("MANIPULATED")
|
2020
|
+
X = decompose(H=qops, U=U)
|
2021
|
+
evals = simulate(X, variables=variables)
|
2022
|
+
else:
|
2023
|
+
evals = simulate(ExpectationValue(H=qops, U=U, shape=[len(qops)]), variables=variables)
|
2024
|
+
else:
|
2025
|
+
qops = [rdm_trafo.dagger()*qops[i]*rdm_trafo for i in range(len(qops))]
|
2026
|
+
evals = simulate(ExpectationValue(H=qops, U=U, shape=[len(qops)]), variables=variables)
|
1998
2027
|
|
1999
2028
|
# Assemble density matrices
|
2000
2029
|
# If self._rdm1, self._rdm2 exist, reset them if they are of the other spin-type
|
@@ -2127,6 +2156,58 @@ class QuantumChemistryBase:
|
|
2127
2156
|
n_ri=n_ri, external_info=external_info, **kwargs)
|
2128
2157
|
return correction.compute()
|
2129
2158
|
|
2159
|
+
def n_rotation(self, i, phi):
|
2160
|
+
'''
|
2161
|
+
Creates a quantum circuit that applies a phase rotation based on phi to both components (up and down) of a given qubit.
|
2162
|
+
|
2163
|
+
Parameters:
|
2164
|
+
- i (int): The index of the qubit to which the rotation will be applied.
|
2165
|
+
- phi (float): The rotation angle. The actual rotation applied will be multiplied with -2 for both components.
|
2166
|
+
|
2167
|
+
Returns:
|
2168
|
+
- QCircuit: A quantum circuit object containing the sequence of rotations applied to the up and down components of the specified qubit.
|
2169
|
+
'''
|
2170
|
+
|
2171
|
+
# Generate number operators for the up and down components of the qubit.
|
2172
|
+
n_up = self.make_number_op(2*i)
|
2173
|
+
n_down = self.make_number_op(2*i+1)
|
2174
|
+
|
2175
|
+
# Start a new circuit and apply rotations to each component.
|
2176
|
+
circuit = gates.GeneralizedRotation(generator = n_up, angle=-2*phi)
|
2177
|
+
circuit += gates.GeneralizedRotation(generator = n_down, angle=-2*phi)
|
2178
|
+
return circuit
|
2179
|
+
|
2180
|
+
def get_givens_circuit(self, unitary, tol = 1e-12, ordering = OPTIMIZED_ORDERING):
|
2181
|
+
'''
|
2182
|
+
Constructs a quantum circuit from a given real unitary matrix using Givens rotations.
|
2183
|
+
|
2184
|
+
This method decomposes a unitary matrix into a series of Givens and Rz (phase) rotations,
|
2185
|
+
then constructs and returns a quantum circuit that implements this sequence of rotations.
|
2186
|
+
|
2187
|
+
Parameters:
|
2188
|
+
- unitary (numpy.array): A real unitary matrix representing the transformation to implement.
|
2189
|
+
- tol (float): A tolerance threshold below which matrix elements are considered zero.
|
2190
|
+
- ordering (list of tuples or 'Optimized'): Custom ordering of indices for Givens rotations or 'Optimized' to generate them automatically.
|
2191
|
+
|
2192
|
+
Returns:
|
2193
|
+
- QCircuit: A quantum circuit implementing the series of rotations decomposed from the unitary.
|
2194
|
+
'''
|
2195
|
+
# Decompose the unitary matrix into Givens and phase (Rz) rotations.
|
2196
|
+
theta_list, phi_list = get_givens_decomposition(unitary, tol, ordering)
|
2197
|
+
|
2198
|
+
# Initialize an empty quantum circuit.
|
2199
|
+
circuit = QCircuit()
|
2200
|
+
|
2201
|
+
# Add all Rz (phase) rotations to the circuit.
|
2202
|
+
for phi in phi_list:
|
2203
|
+
circuit += self.n_rotation(phi[1], phi[0])
|
2204
|
+
|
2205
|
+
# Add all Givens rotations to the circuit.
|
2206
|
+
for theta in reversed(theta_list):
|
2207
|
+
circuit += self.UR(theta[1], theta[2], theta[0]*2)
|
2208
|
+
|
2209
|
+
return circuit
|
2210
|
+
|
2130
2211
|
|
2131
2212
|
def print_basis_info(self):
|
2132
2213
|
return self.integral_manager.print_basis_info()
|
@@ -2147,3 +2228,140 @@ class QuantumChemistryBase:
|
|
2147
2228
|
result += "\nmore information with: self.print_basis_info()\n"
|
2148
2229
|
|
2149
2230
|
return result
|
2231
|
+
|
2232
|
+
def givens_matrix(n, p, q, theta):
|
2233
|
+
'''
|
2234
|
+
Construct a complex Givens rotation matrix of dimension n by theta between rows/columns p and q.
|
2235
|
+
'''
|
2236
|
+
'''
|
2237
|
+
Generates a Givens rotation matrix of size n x n to rotate by angle theta in the (p, q) plane. This matrix can be complex
|
2238
|
+
|
2239
|
+
Parameters:
|
2240
|
+
- n (int): The size of the Givens rotation matrix.
|
2241
|
+
- p (int): The first index for the rotation plane.
|
2242
|
+
- q (int): The second index for the rotation plane.
|
2243
|
+
- theta (float): The rotation angle.
|
2244
|
+
|
2245
|
+
Returns:
|
2246
|
+
- numpy.array: The Givens rotation matrix.
|
2247
|
+
'''
|
2248
|
+
matrix = numpy.eye(n) # Matrix to hold complex numbers
|
2249
|
+
cos_theta = numpy.cos(theta)
|
2250
|
+
sin_theta = numpy.sin(theta)
|
2251
|
+
|
2252
|
+
# Directly assign cosine and sine without complex phase adjustment
|
2253
|
+
matrix[p, p] = cos_theta
|
2254
|
+
matrix[q, q] = cos_theta
|
2255
|
+
matrix[p, q] = sin_theta
|
2256
|
+
matrix[q, p] = -sin_theta
|
2257
|
+
|
2258
|
+
return matrix
|
2259
|
+
|
2260
|
+
def get_givens_decomposition(unitary, tol = 1e-12, ordering = OPTIMIZED_ORDERING, return_diagonal = False):
|
2261
|
+
'''
|
2262
|
+
Decomposes a real unitary matrix into Givens rotations (theta) and Rz rotations (phi).
|
2263
|
+
|
2264
|
+
Parameters:
|
2265
|
+
- unitary (numpy.array): A real unitary matrix to decompose. It cannot be complex.
|
2266
|
+
- tol (float): Tolerance for considering matrix elements as zero. Elements with absolute value less than tol are treated as zero.
|
2267
|
+
- ordering (list of tuples or 'Optimized'): Custom ordering of indices for Givens rotations or 'Optimized' to generate them automatically.
|
2268
|
+
- return_diagonal (bool): If True, the function also returns the diagonal matrix as part of the output.
|
2269
|
+
|
2270
|
+
Returns:
|
2271
|
+
- list: A list of tuples, each representing a Givens rotation. Each tuple contains the rotation angle theta and indices (i,j) of the rotation.
|
2272
|
+
- list: A list of tuples, each representing an Rz rotation. Each tuple contains the rotation angle phi and the index (i) of the rotation.
|
2273
|
+
- numpy.array (optional): The diagonal matrix after applying all Givens rotations, returned if return_diagonal is True.
|
2274
|
+
'''
|
2275
|
+
U = unitary # no need to copy as we don't modify the original
|
2276
|
+
U[abs(U) < tol] = 0 # Zeroing out the small elements as per the tolerance level.
|
2277
|
+
n = U.shape[0]
|
2278
|
+
|
2279
|
+
# Determine optimized ordering if specified.
|
2280
|
+
if ordering == OPTIMIZED_ORDERING:
|
2281
|
+
ordering = ff.depth_eff_order_mf(n)
|
2282
|
+
|
2283
|
+
theta_list = []
|
2284
|
+
phi_list = []
|
2285
|
+
|
2286
|
+
def calcTheta(U, c, r):
|
2287
|
+
'''Calculate and apply the Givens rotation for a specific matrix element.'''
|
2288
|
+
t = numpy.arctan2(-U[r,c], U[r-1,c])
|
2289
|
+
theta_list.append((t, r, r-1))
|
2290
|
+
g = givens_matrix(n,r,r-1,t)
|
2291
|
+
U = numpy.dot(g, U)
|
2292
|
+
|
2293
|
+
return U
|
2294
|
+
|
2295
|
+
# Apply and store Givens rotations as per the given or computed ordering.
|
2296
|
+
if ordering is None:
|
2297
|
+
for c in range(n):
|
2298
|
+
for r in range(n-1, c, -1):
|
2299
|
+
U = calcTheta(U, c, r)
|
2300
|
+
else:
|
2301
|
+
for r, c in ordering:
|
2302
|
+
U = calcTheta(U, c, r)
|
2303
|
+
|
2304
|
+
# Calculating the Rz rotations based on the phases of the diagonal elements.
|
2305
|
+
# For real elements this means a 180 degree shift, i.e. a sign change.
|
2306
|
+
for i in range(n):
|
2307
|
+
ph = numpy.angle(U[i,i])
|
2308
|
+
phi_list.append((ph, i))
|
2309
|
+
|
2310
|
+
# Filtering out rotations without significance.
|
2311
|
+
theta_list_new = []
|
2312
|
+
for i, theta in enumerate(theta_list):
|
2313
|
+
if abs(theta[0] % (2*numpy.pi)) > tol:
|
2314
|
+
theta_list_new.append(theta)
|
2315
|
+
|
2316
|
+
phi_list_new = []
|
2317
|
+
for i, phi in enumerate(phi_list):
|
2318
|
+
if abs(phi[0]) > tol:
|
2319
|
+
phi_list_new.append(phi)
|
2320
|
+
|
2321
|
+
if return_diagonal:
|
2322
|
+
# Optionally return the resulting diagonal
|
2323
|
+
return theta_list_new, phi_list_new, U
|
2324
|
+
else:
|
2325
|
+
return theta_list_new, phi_list_new
|
2326
|
+
|
2327
|
+
def reconstruct_matrix_from_givens(n, theta_list, phi_list, to_real_if_possible = True, tol = 1e-12):
|
2328
|
+
'''
|
2329
|
+
Reconstructs a matrix from given Givens rotations and Rz diagonal rotations.
|
2330
|
+
This function is effectively an inverse of get_givens_decomposition, and therefore only works with data in the same format as its output.
|
2331
|
+
|
2332
|
+
Parameters:
|
2333
|
+
- n (int): The size of the unitary matrix to be reconstructed.
|
2334
|
+
- theta_list (list of tuples): Each tuple contains (angle, i, j) representing a Givens rotation of `angle` radians, applied to rows/columns `i` and `j`.
|
2335
|
+
- phi_list (list of tuples): Each tuple contains (angle, i), representing an Rz rotation by `angle` radians applied to the `i`th diagonal element.
|
2336
|
+
- to_real_if_possible (bool): If True, converts the matrix to real if its imaginary part is effectively zero.
|
2337
|
+
- tol (float): The tolerance whether to swap a complex rotation for a sign change.
|
2338
|
+
|
2339
|
+
Returns:
|
2340
|
+
- numpy.ndarray: The reconstructed complex or real matrix, depending on the `to_real_if_possible` flag and matrix composition.
|
2341
|
+
'''
|
2342
|
+
# Start with an identity matrix
|
2343
|
+
reconstructed = numpy.eye(n, dtype=complex)
|
2344
|
+
|
2345
|
+
# Apply Rz rotations for diagonal elements
|
2346
|
+
for phi in phi_list:
|
2347
|
+
angle, i = phi
|
2348
|
+
# Directly apply a sign flip if the rotation angle is π
|
2349
|
+
if numpy.isclose(angle, numpy.pi, atol=tol):
|
2350
|
+
reconstructed[i, i] *= -1
|
2351
|
+
else:
|
2352
|
+
reconstructed[i, i] *= numpy.exp(1j * angle)
|
2353
|
+
|
2354
|
+
# Apply Givens rotations in reverse order
|
2355
|
+
for theta in reversed(theta_list):
|
2356
|
+
angle, i, j = theta
|
2357
|
+
g = givens_matrix(n, i, j, angle)
|
2358
|
+
reconstructed = numpy.dot(g.conj().T, reconstructed) # Transpose of Givens matrix applied to the left
|
2359
|
+
|
2360
|
+
# Convert matrix to real if its imaginary part is negligible unless disabled via to_real_if_possible
|
2361
|
+
if to_real_if_possible:
|
2362
|
+
# Directly apply a sign flip if the rotation angle is π
|
2363
|
+
if numpy.all(reconstructed.imag == 0):
|
2364
|
+
# Convert to real by taking the real part
|
2365
|
+
reconstructed = reconstructed.real
|
2366
|
+
|
2367
|
+
return reconstructed
|
@@ -36,9 +36,12 @@ try:
|
|
36
36
|
HAS_QISKIT = True
|
37
37
|
INSTALLED_SIMULATORS["qiskit"] = BackendTypes(BackendCircuitQiskit, BackendExpectationValueQiskit)
|
38
38
|
INSTALLED_SAMPLERS["qiskit"] = BackendTypes(BackendCircuitQiskit, BackendExpectationValueQiskit)
|
39
|
-
|
39
|
+
from tequila.simulators.simulator_qiskit import HAS_NOISE as HAS_QISKIT_NOISE
|
40
|
+
if HAS_QISKIT_NOISE:
|
41
|
+
INSTALLED_NOISE_SAMPLERS["qiskit"] = BackendTypes(BackendCircuitQiskit, BackendExpectationValueQiskit)
|
40
42
|
except ImportError:
|
41
43
|
HAS_QISKIT = False
|
44
|
+
HAS_QISKIT_NOISE = False
|
42
45
|
|
43
46
|
HAS_QIBO = True
|
44
47
|
try:
|
@@ -133,6 +136,8 @@ def show_available_simulators():
|
|
133
136
|
str(k in INSTALLED_SAMPLERS),
|
134
137
|
str(k in INSTALLED_NOISE_SAMPLERS),
|
135
138
|
str(k in INSTALLED_BACKENDS)))
|
139
|
+
if HAS_QISKIT and not HAS_QISKIT_NOISE:
|
140
|
+
print("missing qiskit_aer: no noisy simulation")
|
136
141
|
|
137
142
|
|
138
143
|
def pick_backend(backend: str = None, samples: int = None, noise: NoiseModel = None, device=None,
|
@@ -3,12 +3,20 @@ from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
|
|
3
3
|
from tequila import TequilaException, TequilaWarning
|
4
4
|
from tequila import BitString, BitNumbering, BitStringLSB
|
5
5
|
from tequila.utils.keymap import KeyMapRegisterToSubregister
|
6
|
-
import qiskit, numpy, warnings
|
7
|
-
import qiskit.providers.aer.noise as qiskitnoise
|
8
6
|
from tequila.utils import to_float
|
9
|
-
import qiskit
|
10
|
-
from qiskit.providers.ibmq import IBMQBackend
|
7
|
+
import qiskit, numpy, warnings
|
11
8
|
|
9
|
+
HAS_NOISE=True
|
10
|
+
try:
|
11
|
+
from qiskit_aer import noise as qiskitnoise
|
12
|
+
except:
|
13
|
+
HAS_NOISE = False
|
14
|
+
|
15
|
+
HAS_IBMQ=True
|
16
|
+
try:
|
17
|
+
from qiskit.providers.ibmq import IBMQBackend
|
18
|
+
except:
|
19
|
+
HAS_IBMQ=False
|
12
20
|
|
13
21
|
def get_bit_flip(p):
|
14
22
|
"""
|
@@ -137,7 +145,7 @@ class BackendCircuitQiskit(BackendCircuit):
|
|
137
145
|
}
|
138
146
|
|
139
147
|
numbering = BitNumbering.LSB
|
140
|
-
|
148
|
+
|
141
149
|
def __init__(self, abstract_circuit: QCircuit, variables, qubit_map=None, noise=None,
|
142
150
|
device=None, *args, **kwargs):
|
143
151
|
"""
|
@@ -169,7 +177,7 @@ class BackendCircuitQiskit(BackendCircuit):
|
|
169
177
|
'Rx': (lambda c: c.rx, lambda c: c.mcrx),
|
170
178
|
'Ry': (lambda c: c.ry, lambda c: c.mcry),
|
171
179
|
'Rz': (lambda c: c.rz, lambda c: c.mcrz),
|
172
|
-
'Phase': (lambda c: c.
|
180
|
+
'Phase': (lambda c: c.p, lambda c: c.cp),
|
173
181
|
'SWAP': (lambda c: c.swap, lambda c: c.cswap),
|
174
182
|
}
|
175
183
|
|
@@ -2,6 +2,7 @@ import numpy as np
|
|
2
2
|
from tequila.circuit import gates
|
3
3
|
from tequila.circuit.circuit import QCircuit
|
4
4
|
from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian
|
5
|
+
from scipy.stats import unitary_group, ortho_group
|
5
6
|
|
6
7
|
def make_random_circuit(n_qubits: int, rotation_gates: list=['rx', 'ry', 'rz'], n_rotations: int=None,
|
7
8
|
enable_controls: bool=None) -> QCircuit:
|
@@ -75,3 +76,19 @@ def make_random_hamiltonian(n_qubits: int , paulis: list=['X','Y','Z'], n_ps: in
|
|
75
76
|
|
76
77
|
H = QubitHamiltonian(ham)
|
77
78
|
return H
|
79
|
+
|
80
|
+
def generate_random_unitary(size, complex = False):
|
81
|
+
'''
|
82
|
+
Generates a random unitary (or furthermore orthogonal if complex is False) matrix of a specified size.
|
83
|
+
|
84
|
+
Parameters:
|
85
|
+
- size (int): The size of the unitary matrix to be generated.
|
86
|
+
- complex (bool, optional): Whether the unitary should be complex.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
- numpy.ndarray: A randomly generated unitary matrix.
|
90
|
+
'''
|
91
|
+
if complex:
|
92
|
+
return unitary_group.rvs(size)
|
93
|
+
else:
|
94
|
+
return ortho_group.rvs(size)
|
tequila/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
__version__ = "1.9.
|
1
|
+
__version__ = "1.9.6"
|
2
2
|
__author__ = "Tequila Developers "
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tequila-basic
|
3
|
-
Version: 1.9.
|
3
|
+
Version: 1.9.6
|
4
4
|
Summary: A High-Level Abstraction Framework for Quantum Algorithms
|
5
5
|
Home-page: https://github.com/tequilahub/tequila
|
6
6
|
Author: Tequila Developers
|
@@ -32,7 +32,7 @@ Tequila can execute the underlying quantum expectation values on state of the ar
|
|
32
32
|
- [talks and slides](https://kottmanj.github.io/talks_and_material/)
|
33
33
|
|
34
34
|
# Installation
|
35
|
-
Recommended Python version is 3.9
|
35
|
+
Recommended Python version is 3.9 (3.10).
|
36
36
|
Tequila supports linux, osx and windows. However, not all optional dependencies are supported on windows.
|
37
37
|
|
38
38
|
## Install from PyPi
|
@@ -269,11 +269,7 @@ Quantum Equation of Motion with Orbital Optimization for Computing Molecular Pro
|
|
269
269
|
Let us know, if you want your research project and/or tutorial to be included in this list!
|
270
270
|
|
271
271
|
# Dependencies
|
272
|
-
Support for
|
273
|
-
Tequila will then detect them automatically.
|
274
|
-
Currently those are: [Phoenics](https://github.com/aspuru-guzik-group/phoenics)
|
275
|
-
and [GPyOpt](https://sheffieldml.github.io/GPyOpt/).
|
276
|
-
Quantum backends are treated in the same way.
|
272
|
+
Support for specific backends (quantum simulators, optimizers, quantum chemistry) can be activated by intalling them in your environment.
|
277
273
|
|
278
274
|
## Quantum Backends
|
279
275
|
Currently supported
|
@@ -407,5 +403,3 @@ Tequila runs on Mac OSX.
|
|
407
403
|
You might get in trouble with installing qulacs since it currently does not work with Apple's clang compiler.
|
408
404
|
You need to install latest GNU compile (at least gcc-7 and g++7) and set them as default before installing qulacs over pip.
|
409
405
|
|
410
|
-
## Qibo and GPyOpt
|
411
|
-
Currently you can't use Qibo and GPyOpt within the same environment.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
tequila/__init__.py,sha256=FV8-j7GEw_VYadsZUp3M2mGRQxVUYYG3W1jiI6in3CY,1959
|
2
2
|
tequila/autograd_imports.py,sha256=t7V5uYaI0GzjD7pSjkYtiaj3BzSvkm_RL2KcYfNwNhM,1529
|
3
|
-
tequila/version.py,sha256=
|
3
|
+
tequila/version.py,sha256=eF54kEK_euvFMukAbk5bfXdISBhBIxw4DzJuY9f-z3c,57
|
4
4
|
tequila/apps/__init__.py,sha256=GJb04napv8AAx5EHxS5C1CMv9kxQeu7aA-ZMWk6X_eQ,1623
|
5
5
|
tequila/apps/_unary_state_prep_impl.py,sha256=SzRtI0Nx29ODygvYYdC1NnSTCL70wY7NTAvqhiwpMDs,21757
|
6
6
|
tequila/apps/unary_state_prep.py,sha256=QCrD9Ty2RkXc1Mh_MitFPIdaPs_fLxp_dtWVBZi0tSE,9403
|
@@ -15,14 +15,14 @@ tequila/circuit/__init__.py,sha256=ZgoUEjU_NggxjKYkJjh5aO-6VdUjnO5CLxKB44rHzQc,3
|
|
15
15
|
tequila/circuit/_gates_impl.py,sha256=iB40Sg9l9UoCCayu0l4vGjgvEHUVVbXMaGdUtXIHa1w,17821
|
16
16
|
tequila/circuit/circuit.py,sha256=u1qwDFNrZspgea4BFcxwG-hk43ZKAtO2GVCchuctu4s,32044
|
17
17
|
tequila/circuit/compiler.py,sha256=fsHnnNZo43VWUl_n34P21b4GAi7k2kwoYdx0xlbAuZY,32685
|
18
|
-
tequila/circuit/gates.py,sha256=
|
18
|
+
tequila/circuit/gates.py,sha256=buNzNZaflRzVFe1oihCprUBZVL5KcZXyd_kqiWkNbTo,35923
|
19
19
|
tequila/circuit/gradient.py,sha256=Y4dNL6nkZUEkKJvaA3hxaSEa8_b_3XZwxy3j8tGsOmA,10465
|
20
20
|
tequila/circuit/noise.py,sha256=2LJ7Xq5f78x9p4imIz76l_lABQZwgNteNBFkWmUAQvo,8322
|
21
21
|
tequila/circuit/pyzx.py,sha256=XHhKoklhEcbpYkgkWHBLmKF-vyt_Oz-pX-Ctvd4nAOQ,1472
|
22
22
|
tequila/circuit/qasm.py,sha256=iB5stV6Exo3bAn7ac1PgFKH53XD-57b9lpmzjavJK7k,15418
|
23
|
-
tequila/circuit/qpic.py,sha256=
|
23
|
+
tequila/circuit/qpic.py,sha256=sUMXsuYqIgtYF1Cs37Trxtaw6xHNjR4ykrM2fB4T2-I,10787
|
24
24
|
tequila/grouping/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
25
|
-
tequila/grouping/binary_rep.py,sha256=
|
25
|
+
tequila/grouping/binary_rep.py,sha256=MiiAr_xie2fanscfJN0EKToGlCH7po7dEOZKE7c81vM,19411
|
26
26
|
tequila/grouping/binary_utils.py,sha256=ubV8zv3nmz2o74A2W69LeCH6bIZZLHw48lk8ROv-jas,10916
|
27
27
|
tequila/grouping/compile_groups.py,sha256=a9it48zZxJs167nz1FWS_oEmPHPuXq0oLaCs1hJgyBE,66415
|
28
28
|
tequila/grouping/ev_utils.py,sha256=4pnzjRYyOB4KTZsfhs7YSU8Ez4jCJP3YSFKtMOjSwgY,8968
|
@@ -48,20 +48,20 @@ tequila/optimizers/optimizer_gd.py,sha256=QF84K2XDn_-0w9htDlEpIaaGGtfudNamIvVCgx
|
|
48
48
|
tequila/optimizers/optimizer_gpyopt.py,sha256=-aVuutZUScyo6_4q4PyvMMa_OVd3L8ev9Ge2fXyWGV8,11280
|
49
49
|
tequila/optimizers/optimizer_scipy.py,sha256=zqRVQ-Whr74KCGP7Zg1jQkl9S3j9s1kS4oCrCtX30gY,18949
|
50
50
|
tequila/quantumchemistry/__init__.py,sha256=KP99PNJYVwBcfQGwL-MB9IBLSBJNRPf-5WN9NLqT-Rg,7424
|
51
|
-
tequila/quantumchemistry/chemistry_tools.py,sha256=
|
52
|
-
tequila/quantumchemistry/encodings.py,sha256=
|
53
|
-
tequila/quantumchemistry/madness_interface.py,sha256=
|
51
|
+
tequila/quantumchemistry/chemistry_tools.py,sha256=thHsovI-pMJJbEFBuYUAXg2ptitnGe9CeD0mdsh3iV8,46989
|
52
|
+
tequila/quantumchemistry/encodings.py,sha256=YPpFjo4GacWYNxIvDSe6PhNSWBKu6Zm4IlEdy51eUJM,11577
|
53
|
+
tequila/quantumchemistry/madness_interface.py,sha256=ivwDoaUDyAB6lJoFri0pZpvOZbihGavIuybcc40xTD0,36372
|
54
54
|
tequila/quantumchemistry/orbital_optimizer.py,sha256=P_y4Q1qK-C46wgBjL4FBnSHeVSoKlzmkCG5eQWFU4_k,12376
|
55
55
|
tequila/quantumchemistry/psi4_interface.py,sha256=syNaDvlSmCsyB4f7idn3VGbMKyKo83vJHD5y5LpHwaM,29953
|
56
56
|
tequila/quantumchemistry/pyscf_interface.py,sha256=XgecUnKKg2rGiKqsYCJnDQ89ekkDkKuwc3qHULLMYck,6152
|
57
|
-
tequila/quantumchemistry/qc_base.py,sha256=
|
57
|
+
tequila/quantumchemistry/qc_base.py,sha256=x8bQUW35rlHeqd2NheWQrr7cDWYf_niU4FOR5HY2Dgk,106384
|
58
58
|
tequila/simulators/__init__.py,sha256=VFw4sJIt4Zc0-__eYnksN8Ku9qMhbPpHJEkXMWUiD30,4
|
59
|
-
tequila/simulators/simulator_api.py,sha256=
|
59
|
+
tequila/simulators/simulator_api.py,sha256=Rvaljb2yFVMLHzj7ImzMCiTbY1fN59wcxFrE67n-QcU,24413
|
60
60
|
tequila/simulators/simulator_base.py,sha256=93d-f4fNkJ2CtpL9OpgKcypmZH96Mdd9ESdZYn9jH48,33174
|
61
61
|
tequila/simulators/simulator_cirq.py,sha256=z8G3dtZluaQZWPaNs4o3SBKfC33GClC-nplVOyhI1sg,16535
|
62
62
|
tequila/simulators/simulator_pyquil.py,sha256=Xm8fDg9PTFOg3apzd0T8hNKHy4sFrJIbSdnLtvtm-HM,24240
|
63
63
|
tequila/simulators/simulator_qibo.py,sha256=evOGd_-uyUQaQu60K02KIIqtTqjn1jW6XS9ux2TsC9M,25095
|
64
|
-
tequila/simulators/simulator_qiskit.py,sha256=
|
64
|
+
tequila/simulators/simulator_qiskit.py,sha256=d96aqOyQFyqM1W7aJBN_uhBYzt-WxsCZTlhEM-pJhLQ,23506
|
65
65
|
tequila/simulators/simulator_qlm.py,sha256=28c7GmRGWWkbVrvtaw9S4ERkL0fg-TGceayybtQ4K6w,15398
|
66
66
|
tequila/simulators/simulator_qulacs.py,sha256=0XdCA3y7KCqYfL7S4FOssZgxAgs0Hd1GohleNZHSRuA,19728
|
67
67
|
tequila/simulators/simulator_qulacs_gpu.py,sha256=J-QB4jWeZL176G7UFtPkCEReGJXA0mY4iDavfUF07Oo,659
|
@@ -69,7 +69,7 @@ tequila/simulators/simulator_symbolic.py,sha256=H5E9OwFdBYV1lovNPMK3a8cyU8m_EIHm
|
|
69
69
|
tequila/tools/__init__.py,sha256=mJChgkme6v6Ohj2s3tJ96Mq4U0kmVmc6d8Y6_1pEZMg,72
|
70
70
|
tequila/tools/convenience.py,sha256=9WYQJvx5i2QwQG8c7lssYTUv5hzW8h16JTr1GHxYf18,1291
|
71
71
|
tequila/tools/qng.py,sha256=p19A1rcKo06DAyBVUCQzNhwIGni6eAT6IGPnEPhKa4Y,21326
|
72
|
-
tequila/tools/random_generators.py,sha256=
|
72
|
+
tequila/tools/random_generators.py,sha256=UwkDMmNxxSPgaH1LNBswZUiHnLDd8arI3F5QshfBtBY,3417
|
73
73
|
tequila/utils/__init__.py,sha256=nlulDMHcq-XyBZf3NPx2bn_tcixi2mvhRc6MhU9TBWI,317
|
74
74
|
tequila/utils/bitstrings.py,sha256=b2qaicAuDvzb9guTCZvQ1XCgN3dHaJuQ1nlubDgatD4,5689
|
75
75
|
tequila/utils/exceptions.py,sha256=QK7AcDO2-7Itr8Aw6rLP_WtSkH6rfT8PHWcvNM6sQnc,1188
|
@@ -78,8 +78,8 @@ tequila/utils/keymap.py,sha256=RgQzeHEfRVee0-uoH-QsLYsGsXyMhEp3n33KCH-EV2k,3735
|
|
78
78
|
tequila/utils/misc.py,sha256=e62ASkFReaLJQXnBXzyYukzXZnXNoURsM1luoMeIXiE,919
|
79
79
|
tequila/wavefunction/__init__.py,sha256=q4DVL0lGFg03PogRMYA6S8MQqqmLYQiU9VNOF-YQxfQ,50
|
80
80
|
tequila/wavefunction/qubit_wavefunction.py,sha256=16Y9vRj6Yc6sBAKRUHVXJG4lJLDjWyN1b5fugf2LAmI,11881
|
81
|
-
tequila_basic-1.9.
|
82
|
-
tequila_basic-1.9.
|
83
|
-
tequila_basic-1.9.
|
84
|
-
tequila_basic-1.9.
|
85
|
-
tequila_basic-1.9.
|
81
|
+
tequila_basic-1.9.6.dist-info/LICENSE,sha256=oG1FtUav5_xrym9ByiG5emJDQRcbnAfTB08fRV9TCiE,1114
|
82
|
+
tequila_basic-1.9.6.dist-info/METADATA,sha256=vtmnWlAqZZdQ0Rylr2_fgVF66QaUZOsBY9uOZGYHM_w,19409
|
83
|
+
tequila_basic-1.9.6.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
|
84
|
+
tequila_basic-1.9.6.dist-info/top_level.txt,sha256=VBH0gl6mDMbcLHKlO0yEAqtcq08DqBHz4gRJ9jafl5w,8
|
85
|
+
tequila_basic-1.9.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|