Skip to content

Commit e7a4a2f

Browse files
authored
Merge pull request #357 from tequilahub/v.1.9.6
V.1.9.6
2 parents 887b828 + 21f7b99 commit e7a4a2f

13 files changed

+601
-82
lines changed

Diff for: src/tequila/circuit/gates.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ def QubitExcitation(angle: typing.Union[numbers.Real, Variable, typing.Hashable]
940940
except:
941941
raise Exception("QubitExcitation: Needs an even number of targets")
942942

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))
944944

945945

946946
"""

Diff for: src/tequila/circuit/qpic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def export_to(circuit,
224224
'always_use_generators': True,
225225
'group_together': "BARRIER"
226226
}
227-
elif not hasattr("style", "items"):
227+
elif not hasattr(style, "items"):
228228
raise Exception(
229229
"style needs to be `tequila`, or `standard` or `generators` or a dictionary, you gave: {}".format(
230230
str(style)))

Diff for: src/tequila/grouping/binary_rep.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def init_from_qubit_hamiltonian(cls, hamiltonian: QubitHamiltonian, n_qubits=Non
2626
del Hof.terms[()]
2727
hamiltonian = QubitHamiltonian.from_openfermion(Hof)
2828
if n_qubits is None:
29-
n_qubits = hamiltonian.n_qubits
29+
n_qubits = max(hamiltonian.qubits)+1
3030
binary_terms = [
3131
BinaryPauliString(
3232
p.binary(n_qubits).binary,

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

+122-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import typing
33
import warnings
44
from dataclasses import dataclass
5-
5+
from copy import deepcopy
6+
from numbers import Real
67
import numpy
78

8-
from tequila import BitString, QCircuit, TequilaException
9+
from tequila import BitString, QCircuit, TequilaException,Variable,compile_circuit
910
from tequila.circuit import gates
10-
1111
try:
1212
from openfermion.ops.representations import get_active_space_integrals # needs openfermion 1.3
1313
except ImportError as E:
@@ -50,16 +50,132 @@ def __init__(self, generator, p0, transformation, indices=None, *args, **kwargs)
5050
self._name = "FermionicExcitation"
5151
self.transformation = transformation
5252
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)
5457
def compile(self, *args, **kwargs):
5558
if self.is_convertable_to_qubit_excitation():
5659
target = []
5760
for x in self.indices:
5861
for y in x:
5962
target.append(y)
60-
return gates.QubitExcitation(target=target, angle=-self.parameter, control=self.control)
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))
61112
else:
62-
return gates.Trotterized(generator=self.generator, control=self.control, angle=self.parameter, steps=1)
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
63179

64180
def __str(self):
65181
if self.indices is not None:

0 commit comments

Comments
 (0)