Skip to content

Commit

Permalink
Merge pull request #312 from tequilahub/devel
Browse files Browse the repository at this point in the history
Update Master to v.1.9.0
  • Loading branch information
kottmanj authored Oct 6, 2023
2 parents 7c6f5b2 + 380e1a1 commit c7c00d8
Show file tree
Hide file tree
Showing 22 changed files with 843 additions and 152 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci_backends.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
# myqlm does not work in python3.7
pip install "cirq" "qiskit>=0.30" "qulacs" "qibo==0.1.1"
elif [ $ver -eq 8 ]; then
pip install "cirq" "qiskit>=0.30" "qulacs" "qibo==0.1.1" "myqlm"
pip install "cirq" "qiskit>=0.30" "qulacs" "qibo==0.1.1" "myqlm" "cirq-google"
fi
pip install -e .
- name: Lint with flake8
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci_chemistry_pyscf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ jobs:
python -m pip install 'h5py <= 3.1'
cd tests
ls
pytest test_chemistry.py -m "not dependencies"
pytest test_chemistry.py test_TrotErr.py -m "not dependencies"
pytest test_adapt.py -m "not dependencies"
cd ../
3 changes: 2 additions & 1 deletion .github/workflows/ci_pyquil.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ jobs:
python -m pip install --upgrade pip
pip install --upgrade pytest
pip install -r requirements.txt
pip install "pyquil<3.0"
pip install "pyquil<3.0" # needs updates
pip install -e .
pip install --upgrade pip 'urllib3<2' # issues with old pyquil version otherwise
docker pull rigetti/qvm:edge
docker pull rigetti/quilc
docker run --rm -itd -p 5555:5555 rigetti/quilc -R
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Tequila can execute the underlying quantum expectation values on state of the ar

# Installation
Recommended Python version is 3.8-3.9.
Tequila supports linux, osx and windows. However, not all optional dependencies are supported on windows.
Tequila supports linux, osx and windows. However, not all optional dependencies are supported on windows.

## Install from PyPi
**Do not** install like this: (Minecraft lovers excluded)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ qulacs # default simulator (best integration), remove if the installation gives

#optional quantum backends
#cirq >= 0.9.2 #
#cirq_google
#qiskit>=0.30
#pyquil<3.0 # you also need to install the forest-sdk
#qulacs-gpu # you can't have qulacs and qulacs-gpu at the same time
Expand Down
51 changes: 42 additions & 9 deletions src/tequila/circuit/_gates_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from tequila.objective.objective import Variable, FixedVariable, assign_variable
from tequila.hamiltonian import PauliString, QubitHamiltonian, paulis
from tequila.tools import list_assignment
from numpy import pi, sqrt
import numpy as np

from dataclasses import dataclass

Expand Down Expand Up @@ -234,12 +234,12 @@ def shifted_gates(self, r=None):
if r is None:
r = self.eigenvalues_magnitude

s = pi / (4 * r)
s = np.pi / (4 * r)
if self.is_controlled() and not self.assume_real:
# following https://arxiv.org/abs/2104.05695
shifts = [s, -s, 3 * s, -3 * s]
coeff1 = (sqrt(2) + 1)/sqrt(8) * r
coeff2 = (sqrt(2) - 1)/sqrt(8) * r
coeff1 = (np.sqrt(2) + 1)/np.sqrt(8) * r
coeff2 = (np.sqrt(2) - 1)/np.sqrt(8) * r
coefficients = [coeff1, -coeff1, -coeff2, coeff2]
circuits = []
for i, shift in enumerate(shifts):
Expand Down Expand Up @@ -344,7 +344,7 @@ class PowerGateImpl(ParametrizedGateImpl):

@property
def power(self):
return self.parameter/pi
return self.parameter/np.pi

def __init__(self, name, generator: QubitHamiltonian, target: list, power, control: list = None):
if generator is None:
Expand All @@ -353,7 +353,7 @@ def __init__(self, name, generator: QubitHamiltonian, target: list, power, cont
if name is None:
assert generator is not None
name = str(generator)
super().__init__(name=name, parameter=power * pi, target=target, control=control, generator=generator)
super().__init__(name=name, parameter=power * np.pi, target=target, control=control, generator=generator)


class GeneralizedRotationImpl(DifferentiableGateImpl):
Expand All @@ -376,11 +376,44 @@ def extract_targets(generator):
targets += [k for k in ps.keys()]
return tuple(set(targets))

def __init__(self, angle, generator, control=None, eigenvalues_magnitude=0.5, steps=1, assume_real=False):
super().__init__(eigenvalues_magnitude=eigenvalues_magnitude, assume_real=assume_real, name="GenRot", parameter=angle, target=self.extract_targets(generator), control=control)
def __init__(self, angle, generator, p0=None, control=None, target=None, eigenvalues_magnitude=0.5, steps=1, name="GenRot", assume_real=False):
if target == None:
target = self.extract_targets(generator)
super().__init__(eigenvalues_magnitude=eigenvalues_magnitude, generator=generator, assume_real=assume_real, name=name, parameter=angle, target=target, control=control)
self.steps = steps
self.generator = generator
if control is None and p0 is not None:
# augment p0 for control qubits
# Qp = 1/2(1+Z) = |0><0|
p0 = p0*paulis.Qp(control)
self.p0 = p0

def shifted_gates(self):
if not self.assume_real:
# following https://arxiv.org/abs/2104.05695
s = 0.5 * np.pi
shifts = [s, -s, 3 * s, -3 * s]
coeff1 = 0.25 * (np.sqrt(2) + 1)/np.sqrt(2)
coeff2 = 0.25 * (np.sqrt(2) - 1)/np.sqrt(2)
coefficients = [coeff1, -coeff1, -coeff2, coeff2]
circuits = []
for i, shift in enumerate(shifts):
shifted_gate = copy.deepcopy(self)
shifted_gate.parameter += shift
circuits.append((coefficients[i], shifted_gate))
return circuits

r = 0.25
s = 0.5*np.pi

Up1 = copy.deepcopy(self)
Up1._parameter = self.parameter+s
Up2 = GeneralizedRotationImpl(angle=s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0
Um1 = copy.deepcopy(self)
Um1._parameter = self.parameter-s
Um2 = GeneralizedRotationImpl(angle=-s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0

return [(2.0 * r, [Up1, Up2]), (-2.0 * r, [Um1, Um2])]

class ExponentialPauliGateImpl(DifferentiableGateImpl):
"""
Same convention as for rotation gates:
Expand Down
151 changes: 90 additions & 61 deletions src/tequila/circuit/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,13 @@ def Rp(paulistring: typing.Union[PauliString, str], angle, control: typing.Union
return ExpPauli(paulistring=paulistring, angle=angle, control=control, *args, **kwargs)


def GenRot(*args, **kwargs):
return GeneralizedRotation(*args, **kwargs)

def GeneralizedRotation(angle: typing.Union[typing.List[typing.Hashable], typing.List[numbers.Real]],
generator: QubitHamiltonian,
control: typing.Union[list, int] = None,
eigenvalues_magnitude: float = 0.5,
eigenvalues_magnitude: float = 0.5, p0=None,
steps: int = 1, assume_real=False) -> QCircuit:
"""
Expand Down Expand Up @@ -393,6 +396,8 @@ def GeneralizedRotation(angle: typing.Union[typing.List[typing.Hashable], typing
list of control qubits
eigenvalues_magnitude
magnitude of eigenvalues, in most papers referred to as "r" (default 0.5)
p0
possible nullspace projector (if the rotation is happens in Q = 1-P0). See arxiv:2011.05938
steps
possible Trotterization steps (default 1)
Expand All @@ -403,7 +408,7 @@ def GeneralizedRotation(angle: typing.Union[typing.List[typing.Hashable], typing

return QCircuit.wrap_gate(
impl.GeneralizedRotationImpl(angle=assign_variable(angle), generator=generator, control=control,
eigenvalues_magnitude=eigenvalues_magnitude, steps=steps, assume_real=assume_real))
eigenvalues_magnitude=eigenvalues_magnitude, steps=steps, assume_real=assume_real, p0=p0))



Expand Down Expand Up @@ -473,7 +478,7 @@ def Trotterized(generator: QubitHamiltonian = None,
return QCircuit.wrap_gate(impl.TrotterizedGateImpl(generator=generator, angle=angle, steps=steps, control=control, randomize=randomize, **kwargs))


def SWAP(first: int, second: int, control: typing.Union[int, list] = None, power: float = None, *args,
def SWAP(first: int, second: int, angle: float = None, control: typing.Union[int, list] = None, power: float = None, *args,
**kwargs) -> QCircuit:
"""
Notes
Expand All @@ -486,10 +491,12 @@ def SWAP(first: int, second: int, control: typing.Union[int, list] = None, power
target qubit
second: int
target qubit
angle: numeric type or hashable type
exponent in the for e^{-i a/2 G}
control
int or list of ints
power
numeric type (fixed exponent) or hashable type (parametrized exponent)
numeric type (fixed exponent) or hashable type (parametrized exponent in the form (SWAP)^n
Returns
-------
Expand All @@ -498,12 +505,82 @@ def SWAP(first: int, second: int, control: typing.Union[int, list] = None, power
"""

target = [first, second]
if angle is not None:
assert power is None
angle = assign_variable(angle)
elif power is not None:
angle = assign_variable(power)*np.pi
generator = 0.5 * (paulis.X(target) + paulis.Y(target) + paulis.Z(target) - paulis.I(target))
if power is None or power in [1, 1.0]:
if angle is None or power in [1, 1.0]:
return QGate(name="SWAP", target=target, control=control, generator=generator)
else:
return GeneralizedRotation(angle=power * np.pi, control=control, generator=generator,
return GeneralizedRotation(angle=angle, control=control, generator=generator,
eigenvalues_magnitude=0.25)


def iSWAP(first: int, second: int, control: typing.Union[int, list] = None, power: float = 1.0, *args,
**kwargs) -> QCircuit:
"""
Notes
----------
iSWAP gate
.. math::
iSWAP = e^{i\\frac{\\pi}{4} (X \\otimes X + Y \\otimes Y )}
Parameters
----------
first: int
target qubit
second: int
target qubit
control
int or list of ints
power
numeric type (fixed exponent) or hashable type (parametrized exponent)
Returns
-------
QCircuit
"""

generator = paulis.from_string(f"X({first})X({second}) + Y({first})Y({second})")

p0 = paulis.Projector("|00>") + paulis.Projector("|11>")
p0 = p0.map_qubits({0:first, 1:second})

gate = QubitExcitationImpl(angle=power*(-np.pi/2), target=generator.qubits, generator=generator, p0=p0, control=control, compile_options="vanilla", *args, **kwargs)

return QCircuit.wrap_gate(gate)


def Givens(first: int, second: int, control: typing.Union[int, list] = None, angle: float = None, *args,
**kwargs) -> QCircuit:
"""
Notes
----------
Givens gate G
.. math::
G = e^{-i\\theta \\frac{(Y \\otimes X - X \\otimes Y )}{2}}
Parameters
----------
first: int
target qubit
second: int
target qubit
control
int or list of ints
angle
numeric type (fixed exponent) or hashable type (parametrized exponent), theta in the above formula
Returns
-------
QCircuit
"""

return QubitExcitation(target=[second,first], angle=2*angle, control=control, *args, **kwargs) # twice the angle since theta is not divided by two in the matrix exponential


"""
Expand Down Expand Up @@ -965,11 +1042,7 @@ def QGate(name, target: typing.Union[list, int], control: typing.Union[list, int
returns a QCircuit of primitive tq gates
"""

class QubitExcitationImpl(impl.DifferentiableGateImpl):

@property
def steps(self):
return 1
class QubitExcitationImpl(impl.GeneralizedRotationImpl):

def __init__(self, angle, target, generator=None, p0=None, assume_real=True, control=None, compile_options=None):
angle = assign_variable(angle)
Expand All @@ -990,15 +1063,9 @@ def __init__(self, angle, target, generator=None, p0=None, assume_real=True, con
else:
assert generator is not None
assert p0 is not None

super().__init__(name="QubitExcitation", parameter=angle, target=target, control=control)
self.generator = generator
if control is not None:
# augment p0 for control qubits
# Qp = 1/2(1+Z) = |0><0|
p0 = p0*paulis.Qp(control)
self.p0 = p0
self.assume_real = assume_real

super().__init__(name="QubitExcitation", angle=angle, generator=generator, target=target, p0=p0, control=control, assume_real=assume_real, steps=1)

if compile_options is None:
self.compile_options = "optimize"
elif hasattr(compile_options, "lower"):
Expand All @@ -1007,18 +1074,9 @@ def __init__(self, angle, target, generator=None, p0=None, assume_real=True, con
self.compile_options = compile_options

def map_qubits(self, qubit_map: dict):
mapped_generator = self.generator.map_qubits(qubit_map=qubit_map)
mapped_p0 = self.p0.map_qubits(qubit_map=qubit_map)
mapped_control = self.control
if mapped_control is not None:
mapped_control=tuple([qubit_map[i] for i in self.control])
result = copy.deepcopy(self)
result.generator=mapped_generator
result.p0 = mapped_p0
result._target = tuple([qubit_map[x] for x in self.target])
result._control = mapped_control
result.finalize()
return result
mapped = super().map_qubits(qubit_map)
mapped.p0 = self.p0.map_qubits(qubit_map=qubit_map)
return mapped

def compile(self, exponential_pauli=False, *args, **kwargs):
# optimized compiling for single and double qubit excitaitons following arxiv:2005.14475
Expand All @@ -1040,35 +1098,6 @@ def compile(self, exponential_pauli=False, *args, **kwargs):
else:
return Trotterized(angle=self.parameter, generator=self.generator, steps=1)

def shifted_gates(self):
if not self.assume_real:
# following https://arxiv.org/abs/2104.05695
s = 0.5 * np.pi
shifts = [s, -s, 3 * s, -3 * s]
coeff1 = 0.25 * (np.sqrt(2) + 1)/np.sqrt(2)
coeff2 = 0.25 * (np.sqrt(2) - 1)/np.sqrt(2)
coefficients = [coeff1, -coeff1, -coeff2, coeff2]
circuits = []
for i, shift in enumerate(shifts):
shifted_gate = copy.deepcopy(self)
shifted_gate.parameter += shift
circuits.append((coefficients[i], shifted_gate))
return circuits

r = 0.25
s = 0.5*np.pi

Up1 = copy.deepcopy(self)
Up1._parameter = self.parameter+s
Up1 = QCircuit.wrap_gate(Up1)
Up2 = GeneralizedRotation(angle=s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0
Um1 = copy.deepcopy(self)
Um1._parameter = self.parameter-s
Um1 = QCircuit.wrap_gate(Um1)
Um2 = GeneralizedRotation(angle=-s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0

return [(2.0 * r, Up1 + Up2), (-2.0 * r, Um1 + Um2)]

def _convert_Paulistring(paulistring: typing.Union[PauliString, str, dict]) -> PauliString:
'''
Function that given a paulistring as PauliString structure or
Expand Down
Loading

0 comments on commit c7c00d8

Please sign in to comment.