from tequila import TequilaException
from tequila.circuit.circuit import QCircuit
from tequila.circuit.gates import Rx, Ry, H, X, Rz, ExpPauli, CNOT, Phase, T, Z
from tequila.circuit._gates_impl import RotationGateImpl, PhaseGateImpl, QGateImpl, \
ExponentialPauliGateImpl, TrotterizedGateImpl, PowerGateImpl
from tequila.utils import to_float
from tequila.objective.objective import Variable, FixedVariable
from tequila.objective.objective import Objective
from tequila.objective.objective import ExpectationValueImpl
import numpy
from numpy import pi as pi
import copy, typing
[docs]
class TequilaCompilerException(TequilaException):
pass
[docs]
class CircuitCompiler:
"""
an object that performs abstract compilation of QCircuits and Objectives.
Note
----
see init for attributes, since all are specified there
Methods
-------
compile_objective
perform compilation on an entire objective
compile_objective_argument
perform compilation on a single arg of objective
compile_circuit:
perform compilation on a circuit.
"""
[docs]
@classmethod
def all_flags_true(cls, *args, **kwargs):
# convenience: Initialize with all flags set to true
# set exceptions in kwargs
c = cls()
for k in c.__dict__.keys():
try:
c.__dict__[k]=True
except:
pass
for k,v in kwargs.items():
if k in c.__dict__:
c.__dict__[k]=v
c.gradient_mode=False
if not c.multicontrol:
c.cc_max = False
return c
[docs]
@classmethod
def standard_gate_set(cls, *args, **kwargs):
# convenience: Initialize with all flags set to true
# but not for standard gates like ry
# set exceptions in kwargs
c = cls.all_flags_true()
c.gradient_mode=False
c.y_gate=False
c.ry_gate=False
for k,v in kwargs.items():
if k in c.__dict__:
c.__dict__[k]=v
if not c.multicontrol:
c.cc_max = False
return c
def __init__(self,
multitarget=False,
multicontrol=False,
trotterized=False,
generalized_rotation=False,
exponential_pauli=False,
controlled_exponential_pauli=False,
hadamard_power=False,
controlled_power=False,
power=False,
toffoli=False,
controlled_phase=False,
phase=False,
phase_to_z=False,
controlled_rotation=False,
swap=False,
cc_max=False,
gradient_mode=False,
ry_gate=False,
y_gate=False,
ch_gate=False,
hadamard=False
):
"""
all parameters are booleans.
Parameters
----------
multitarget:
whether or not to split multitarget gates into single target (if gate isn't inherently multitarget)
multicontrol:
whether or not to split gates into single controlled gates.
trotterized:
whether or not to break down TrotterizedGateImpl into other types
generalized_rotation:
whether or not to break down GeneralizedRotationGateImpl into other types
exponential_pauli:
whether or not to break down ExponentialPauliGateImpl into other types
controlled_exponential_pauli
whether or not to break down controlled exponential pauli gates.
hadamard_power:
whether or not to break down Hadamard gates, raised to a power, into other rotation gates.
controlled_power:
whether or not to break down controlled power gates into CNOT and other gates.
power:
whether or not to break down parametrized power gates into rotation gates
toffoli:
whether or not to break down the toffoli gate into CNOTs and other single qubit gates.
controlled_phase:
whether or not to break down controlled phase gates into CNOTs and phase gates.
phase:
whether to replace phase gates
phase_to_z:
specifically, whether to replace phase gates with the z gate
controlled_rotation:
whether or not to break down controlled rotation gates into CNot and single qubit gates
swap:
whether or not to break down swap gates into CNOT gates.
cc_max:
whether or not to break down all controlled gates with 2 or more controls.
ry_gate:
whether or not to break down all rotational y gates
y_gate:
whether or not to break down all y gates
ch_gate:
whether or not to break down all controlled-H gates
"""
self.multitarget = multitarget
self.multicontrol = multicontrol
self.generalized_rotation = generalized_rotation
self.trotterized = trotterized
self.exponential_pauli = exponential_pauli
self.controlled_exponential_pauli = controlled_exponential_pauli
self.hadamard_power = hadamard_power
self.hadamard = hadamard
self.controlled_power = controlled_power
self.power = power
self.toffoli = toffoli
self.controlled_phase = controlled_phase
self.phase = phase
self.phase_to_z = phase_to_z
self.controlled_rotation = controlled_rotation
self.swap = swap
self.cc_max = cc_max
self.gradient_mode = gradient_mode
self.ry_gate = ry_gate
self.y_gate = y_gate
self.ch_gate = ch_gate
def __call__(self, objective: typing.Union[Objective, QCircuit, ExpectationValueImpl], variables=None, *args,
**kwargs):
"""
Perform compilation
Parameters
----------
objective:
the object (not necessarily an objective) to compile.
variables: optional:
Todo: Jakob, what is this for?
args
kwargs
Returns
-------
a compiled version of objective
"""
if isinstance(objective, Objective) or hasattr(objective, "args"):
result = self.compile_objective(objective=objective, variables=variables, *args, **kwargs)
elif isinstance(objective, QCircuit) or hasattr(objective, "gates"):
result = self.compile_circuit(abstract_circuit=objective, variables=variables, *args, **kwargs)
elif isinstance(objective, ExpectationValueImpl) or hasattr(objective, "U"):
result = self.compile_objective_argument(arg=objective, variables=variables, *args, **kwargs)
else:
raise TequilaCompilerException("Tequila compiler can't process type {}".format(type(objective)))
return result
[docs]
def compile_objective(self, objective, *args, **kwargs):
"""
Compile an objective.
Parameters
----------
objective: Objective:
the objective.
args
kwargs
Returns
-------
the objective, compiled
"""
argsets=objective.argsets
compiled_sets=[]
for argset in argsets:
compiled_args = []
already_processed = {}
for arg in argset:
if isinstance(arg, ExpectationValueImpl) or (hasattr(arg, "U") and hasattr(arg, "H")):
if arg in already_processed:
compiled_args.append(already_processed[arg])
else:
compiled = self.compile_objective_argument(arg, *args, **kwargs)
compiled_args.append(compiled)
already_processed[arg] = compiled
else:
# nothing to process for non-expectation-value types, but acts as sanity check
compiled_args.append(self.compile_objective_argument(arg, *args, **kwargs))
compiled_sets.append(compiled_args)
if isinstance(objective,Objective):
return type(objective)(args=compiled_sets[0],transformation=objective.transformation)
[docs]
def compile_objective_argument(self, arg, *args, **kwargs):
"""
Compile an argument of an objective.
Parameters
----------
arg:
the term to compile
args
kwargs
Returns
-------
the arg, compiled
"""
if isinstance(arg, ExpectationValueImpl) or (hasattr(arg, "U") and hasattr(arg, "H")):
return ExpectationValueImpl(H=arg.H,
U=self.compile_circuit(abstract_circuit=arg.U, *args,
**kwargs))
elif hasattr(arg, "abstract_expectationvalue"):
E = arg.abstract_expectationvalue
E._U = self.compile_circuit(abstract_circuit=E.U, *args, **kwargs)
return type(arg)(E, **arg._input_args)
elif isinstance(arg, Variable) or hasattr(arg, "name") or isinstance(arg, FixedVariable):
return arg
else:
raise TequilaCompilerException(
"Unknown argument type for objectives: {arg} or type {type}".format(arg=arg, type=type(arg)))
[docs]
def compile_circuit(self, abstract_circuit: QCircuit, variables=None, *args, **kwargs) -> QCircuit:
"""
compile a circuit.
Parameters
----------
abstract_circuit: QCircuit
the circuit to compile.
variables:
(Default value = None):
list of the variables whose gates, specifically, must compile.
Used to prevent excess compilation in gates whose parameters are fixed.
Default: compile every single gate.
args
kwargs
Returns
-------
QCircuit; a compiled circuit.
"""
n_qubits = abstract_circuit.n_qubits
compiled = QCircuit(abstract_circuit.gates)
if variables is None:
# check & compile all gates
gatelist = enumerate(abstract_circuit.gates)
else:
# check & compile only gates which depend on variables
gatelist = []
for variable in variables:
gatelist += abstract_circuit._parameter_map[variable]
compiled_gates = []
for idx, gate in gatelist:
cg = gate
controlled = gate.is_controlled()
if self.gradient_mode and (hasattr(cg, "eigenvalues_magnitude") or hasattr(cg, "shifted_gates")):
compiled_gates.append((idx, QCircuit.wrap_gate(cg)))
continue
else:
if hasattr(cg, "compile"):
cg = QCircuit.wrap_gate(cg.compile(**self.__dict__))
for g in cg.gates:
if g.is_controlled():
controlled = True
# order matters
# first the real multi-target gates
if controlled or self.trotterized:
cg = compile_trotterized_gate(gate=cg)
if controlled or self.generalized_rotation:
cg = compile_generalized_rotation_gate(gate=cg)
if controlled or self.exponential_pauli:
cg = compile_exponential_pauli_gate(gate=cg)
if self.swap:
cg = compile_swap(gate=cg)
if self.phase_to_z:
cg = compile_phase_to_z(gate=cg)
if self.power:
cg = compile_power_gate(gate=cg)
if self.phase:
cg = compile_phase(gate=cg)
if self.ch_gate:
cg = compile_ch(gate=cg)
if self.y_gate:
cg = compile_y(gate=cg)
if self.ry_gate:
cg = compile_ry(gate=cg, controlled_rotation=self.controlled_rotation)
if controlled:
if self.cc_max or self.multicontrol:
cg = compile_to_single_control(gate=cg)
if self.controlled_exponential_pauli:
cg = compile_exponential_pauli_gate(gate=cg)
if self.controlled_power:
cg = compile_controlled_power(gate=cg)
if self.controlled_phase:
cg = compile_controlled_phase(gate=cg)
if self.phase:
cg = compile_phase(gate=cg)
if self.toffoli:
cg = compile_toffoli(gate=cg)
if self.phase:
cg = compile_phase(gate=cg)
if self.controlled_rotation:
cg = compile_controlled_rotation(gate=cg)
compiled_gates.append((idx, cg))
if len(compiled_gates) == 0:
return abstract_circuit
else:
pos, cgs = zip(*compiled_gates)
compiled = abstract_circuit.replace_gates(positions=pos, circuits=cgs)
return compiled
[docs]
def compiler(f):
"""
Decorator for compile functions.
Make them applicable for single gates as well as for whole circuits
Note that all arguments need to be passed as keyword arguments
"""
def wrapper(gate, **kwargs):
if hasattr(gate, "gates"):
result = QCircuit()
for g in gate.gates:
result += f(gate=g, **kwargs)
return result
elif hasattr(gate, 'U'):
cU = QCircuit()
for g in gate.U.gates:
cU += f(gate=g, **kwargs)
return type(gate)(U=cU, H=gate.H)
elif hasattr(gate, 'transformations'):
outer=[]
for args in gate.argsets:
compiled = []
for E in args:
if hasattr(E, 'name'):
compiled.append(E)
else:
cU = QCircuit()
for g in E.U.gates:
cU += f(gate=g, **kwargs)
compiled.append(type(E)(U=cU, H=E.H))
outer.append(compiled)
if isinstance(gate, Objective):
return type(gate)(args=outer[0], transformation=gate._transformation)
else:
return f(gate=gate, **kwargs)
return wrapper
[docs]
def change_basis(target, axis=None, name=None, daggered=False):
"""
helper function; returns circuit that performs change of basis.
Parameters
----------
target:
the qubit having its basis changed
axis:
The axis of rotation to shift into.
daggered: bool:
adjusts the sign of the gate if axis = 1, I.E, change of basis about Y axis.
Returns
-------
QCircuit that performs change of basis on target qubit onto desired axis
"""
if axis is None and name is None:
raise TequilaException('axis or name must be given.')
if name:
name = name.lower()
if name in ['h', 'hadamard'] and daggered:
return Ry(angle=numpy.pi / 4, target=target)
elif name in ['h', 'hadamard']:
return Ry(angle=-numpy.pi / 4, target=target)
else:
name_to_axis = {'rx': 0, 'ry': 1, 'rz': 2}
axis = name_to_axis.get(name, name)
if isinstance(axis, str):
axis = RotationGateImpl.string_to_axis[axis.lower()]
if axis == 0 and daggered:
return Ry(angle=numpy.pi / 2, target=target)
elif axis == 0:
return Ry(angle=-numpy.pi / 2, target=target)
elif axis == 1 and daggered:
return Rx(angle=-numpy.pi / 2, target=target)
elif axis == 1:
return Rx(angle=numpy.pi / 2, target=target)
else:
return QCircuit()
[docs]
@compiler
def compile_multitarget(gate, *args, **kwargs) -> QCircuit:
"""
If a gate is 'trivially' multitarget, split it into single target gates.
Parameters
----------
gate:
the gate in question
Returns
-------
QCircuit, the result of compilation.
"""
targets = gate.target
# don't compile real multitarget gates
if hasattr(gate, "generator") or hasattr(gate, "generators") or hasattr(gate, "paulistring"):
return QCircuit.wrap_gate(gate)
if isinstance(gate, ExponentialPauliGateImpl) or isinstance(gate, TrotterizedGateImpl):
return QCircuit.wrap_gate(gate)
if len(targets) == 1:
return QCircuit.wrap_gate(gate)
if gate.name.lower() in ["swap", "iswap"]:
return QCircuit.wrap_gate(gate)
result = QCircuit()
for t in targets:
gx = copy.deepcopy(gate)
gx._target = (t,)
result += gx
return result
# return index of control qubits in Gray Code order.
def _pattern(n):
if n == 1:
return [0]
pn = _pattern(n - 1)
return pn + [n - 1] + pn
[docs]
@compiler
def compile_controlled_rotation(gate: RotationGateImpl) -> QCircuit:
"""
Recompilation of a controlled-rotation gate
Basis change into Rz then recompilation of controled Rz, then change basis back
:param gate: The rotational gate
:return: set of gates wrapped in QCircuit class
"""
if not gate.is_controlled():
return QCircuit.wrap_gate(gate)
if not isinstance(gate, RotationGateImpl):
return QCircuit.wrap_gate(gate)
if len(gate.target) > 1:
return compile_controlled_rotation(gate=compile_multitarget(gate=gate))
target = gate.target
control = gate.control
k = len(control)
cind = _pattern(k) + [k - 1]
result = QCircuit()
result += change_basis(target=target, axis=gate._axis)
coeff = - 1 / pow(2, k)
for i, ci in enumerate(cind):
coeff *= -1
result += Rz(target=target, angle=coeff * gate.parameter)
result += CNOT(control[ci], target)
result += change_basis(target=target, axis=gate._axis, daggered=True)
result.n_qubits = result.max_qubit() + 1
return result
[docs]
@compiler
def compile_to_single_control(gate) -> QCircuit:
"""
break down a gate into a sequence with no more than single-controlled gates.
Parameters
----------
gate:
the gate.
Returns
-------
A QCircuit; the result of compilation.
"""
if not gate.is_controlled:
return QCircuit.wrap_gate(gate)
cl = len(gate.control)
target = gate.target
control = gate.control
if cl <= 1:
return QCircuit.wrap_gate(gate)
name = gate.name
back = QCircuit()
if name in ['X', 'x', 'Y', 'y', 'Z', 'z', 'H', 'h']:
if isinstance(gate, PowerGateImpl):
power = gate.parameter
else:
power = 1.0
new = PowerGateImpl(name=name, power=power, target=target, control=control, generator=gate.make_generator())
partial = compile_power_gate(gate=new)
back += compile_to_single_control(gate=partial)
elif isinstance(gate, RotationGateImpl):
partial = compile_controlled_rotation(gate=gate)
back += compile_to_single_control(gate=partial)
elif isinstance(gate, PhaseGateImpl):
partial = compile_controlled_phase(gate=gate)
back += compile_to_single_control(gate=partial)
else:
print(gate)
raise TequilaException('frankly, what the fuck is this gate?')
return back
[docs]
@compiler
def compile_toffoli(gate) -> QCircuit:
"""
break down a toffoli gate into a sequence of CNOT and single qubit gates.
Parameters
----------
gate:
the gate.
Returns
-------
A QCircuit; the result of compilation.
"""
if gate.name.lower != 'x':
return QCircuit.wrap_gate(gate)
control = gate.control
c1 = control[1]
c0 = control[0]
target = gate.target
result = QCircuit()
result += H(target)
result += CNOT(c1, target)
result += T(target).dagger()
result += CNOT(c0, target)
result += T(target)
result += CNOT(c1, target)
result += T(target).dagger()
result += CNOT(c0, target)
result += T(c1)
result += T(target)
result += CNOT(c0, c1)
result += H(target)
result += T(c0)
result += T(c1).dagger()
result += CNOT(c0, c1)
return (result)
[docs]
@compiler
def compile_power_gate(gate) -> QCircuit:
"""
break down power gates into the rotation gates.
Parameters
----------
gate:
the gate.
Returns
-------
A QCircuit; the result of compilation.
"""
if not isinstance(gate, PowerGateImpl):
return QCircuit.wrap_gate(gate)
if not gate.is_controlled():
return compile_power_base(gate=gate)
return compile_controlled_power(gate=gate)
[docs]
@compiler
def compile_power_base(gate):
"""
Base case of compile_power_gate: convert a 1-qubit parametrized power gate into rotation gates.
Parameters
----------
gate:
the gate.
Returns
-------
A QCircuit; the result of compilation.
"""
if not isinstance(gate, PowerGateImpl):
return QCircuit.wrap_gate(gate)
if gate.is_controlled():
return QCircuit.wrap_gate(gate)
power = gate.power
if gate.name.lower() in ['h', 'hadamard']:
### off by global phase of Exp[ pi power /2]
theta = power * numpy.pi
result = QCircuit()
result += Ry(angle=-numpy.pi / 4, target=gate.target)
result += Rz(angle=theta, target=gate.target)
result += Ry(angle=numpy.pi / 4, target=gate.target)
elif gate.name == 'X':
### off by global phase of Exp[ pi power /2]
'''
if we wanted to do it formally we would use the following
a=-numpy.pi/2
b=numpy.pi/2
theta = power*numpy.pi
result = QCircuit()
result+= Rz(angle=b,target=gate.target)
result+= Ry(angle=theta,target=gate.target)
result+= Rz(angle=a,target=gate.target)
'''
result = Rx(angle=power * numpy.pi, target=gate.target)
elif gate.name == 'Y':
### off by global phase of Exp[ pi power /2]
theta = power * numpy.pi
result = QCircuit()
result += Ry(angle=theta, target=gate.target)
elif gate.name == 'Z':
### off by global phase of Exp[ pi power /2]
a = 0
b = power * numpy.pi
theta = 0
result = QCircuit()
result += Rz(angle=b, target=gate.target)
else:
raise TequilaException('passed a gate with name ' + gate.name + ', which cannot be handled!')
return result
[docs]
@compiler
def compile_controlled_power(gate: PowerGateImpl) -> QCircuit:
"""
Recompilation of a controlled-power gate
Basis change into Z then recompilation of controled Z, then change basis back
:param gate: The power gate
:return: set of gates wrapped in QCircuit class
"""
if not gate.is_controlled():
return QCircuit.wrap_gate(gate)
if not isinstance(gate, PowerGateImpl):
return QCircuit.wrap_gate(gate)
if len(gate.target) > 1:
return compile_controlled_power(gate=compile_multitarget(gate=gate))
power = gate.power
target = gate.target
control = gate.control
result = QCircuit()
result += Phase(target=control[0], control=control[1:], phi=power * pi / 2)
result += change_basis(target=target, name=gate.name)
result += Rz(target=target, control=control, angle=power * pi)
result += change_basis(target=target, name=gate.name, daggered=True)
result.n_qubits = result.max_qubit() + 1
return result
[docs]
@compiler
def compile_phase(gate) -> QCircuit:
"""
Compile phase gates into Rz gates and cnots, if controlled
Parameters
----------
gate:
the gate
Returns
-------
QCircuit, the result of compilation.
"""
if not isinstance(gate, PhaseGateImpl):
return QCircuit.wrap_gate(gate)
phase = gate.parameter
result = QCircuit()
if len(gate.control) == 0:
return Rz(angle=phase, target=gate.target)
result = compile_controlled_phase(gate)
result = compile_phase(result)
return result
[docs]
@compiler
def compile_controlled_phase(gate) -> QCircuit:
"""
Compile multi-controlled phase gates to 1q - phase gate and multi-controlled Rz gates.
Parameters
----------
gate:
the gate.
Returns
-------
QCircuit, the result of compilation.
"""
if not isinstance(gate, PhaseGateImpl):
return QCircuit.wrap_gate(gate)
if len(gate.control) == 0:
return QCircuit.wrap_gate(gate)
phase = gate.parameter
result = QCircuit()
result += Phase(target=gate.control[0], control=gate.control[1:], phi=phase / 2)
result += Rz(target=gate.target, control=gate.control, angle=phase)
return compile_controlled_phase(result)
[docs]
@compiler
def compile_phase_to_z(gate) -> QCircuit:
"""
Compile phase gate to parametrized Z gate.
Parameters
----------
gate:
the gate.
Returns
-------
QCircuit, the result of compilation.
"""
if not isinstance(gate, PhaseGateImpl):
return QCircuit.wrap_gate(gate)
phase = gate.parameter
return Z(power=phase / pi, target=gate.target, control=gate.control)
[docs]
@compiler
def compile_swap(gate) -> QCircuit:
"""
Compile swap gates into CNOT.
Parameters
----------
gate:
the gate.
Returns
-------
QCircuit, the result of compilation.
"""
if gate.name.lower() == "swap":
if len(gate.target) != 2:
raise TequilaCompilerException("SWAP gates needs two targets")
power = 1
if hasattr(gate, "power"):
if power is None or power in [1, 1.0]:
pass
else:
raise TequilaCompilerException("Parametrized SWAPs should be decomposed on top level! Something went wrong")
c = []
if gate.control is not None:
c = gate.control
return X(target=gate.target[0], control=[gate.target[1]]) \
+ X(target=gate.target[1], control=[gate.target[0]] + list(c), power=power) \
+ X(target=gate.target[0], control=[gate.target[1]])
else:
return QCircuit.wrap_gate(gate)
[docs]
@compiler
def compile_exponential_pauli_gate(gate) -> QCircuit:
"""
Returns the circuit: exp(i*angle*paulistring)
primitively compiled into X,Y Basis Changes and CNOTs and Z Rotations
:param paulistring: The paulistring in given as tuple of tuples (openfermion format)
like e.g ( (0, 'Y'), (1, 'X'), (5, 'Z') )
:param angle: The angle which parametrizes the gate -> should be real
:returns: the above mentioned circuit as abstract structure
"""
if hasattr(gate, "paulistring"):
angle = gate.paulistring.coeff * gate.parameter
circuit = QCircuit()
# the general circuit will look like:
# series which changes the basis if necessary
# series of CNOTS associated with basis changes
# Rz gate parametrized on the angle
# series of CNOT (inverted direction compared to before)
# series which changes the basis back
ubasis = QCircuit()
ubasis_t = QCircuit()
cnot_cascade = QCircuit()
last_qubit = None
previous_qubit = None
for k, v in gate.paulistring.items():
pauli = v
qubit = [k] # wrap in list for targets= ...
# see if we need to change the basis
axis = 2
if pauli.upper() == "X":
axis = 0
elif pauli.upper() == "Y":
axis = 1
ubasis += change_basis(target=qubit, axis=axis)
ubasis_t += change_basis(target=qubit, axis=axis, daggered=True)
if previous_qubit is not None:
cnot_cascade += X(target=qubit, control=previous_qubit)
previous_qubit = qubit
last_qubit = qubit
reversed_cnot = cnot_cascade.dagger()
# assemble the circuit
circuit += ubasis
circuit += cnot_cascade
circuit += Rz(target=last_qubit, angle=angle, control=gate.control)
circuit += reversed_cnot
circuit += ubasis_t
return circuit
else:
return QCircuit.wrap_gate(gate)
[docs]
def do_compile_trotterized_gate(generator, steps, factor, randomize, control):
assert (generator.is_hermitian())
circuit = QCircuit()
factor = factor / steps
for index in range(steps):
paulistrings = generator.paulistrings
if randomize:
numpy.random.shuffle(paulistrings)
for ps in paulistrings:
coeff = to_float(ps.coeff)
if len(ps._data) == 0 and len(control) > 0:
circuit += Phase(target=control[0], control=control[1:], phi=-factor * coeff / 2)
elif len(ps._data) > 0:
circuit += ExpPauli(paulistring=ps.naked(), angle=factor * coeff, control=control)
else:
# ignore global phases
pass
return circuit
[docs]
@compiler
def compile_generalized_rotation_gate(gate, compile_exponential_pauli: bool = False):
"""
Parameters
----------
gate
compile_exponential_pauli
Returns
-------
"""
if gate.generator is None or gate.name.lower() in ['phase', 'rx', 'ry', 'rz']:
return QCircuit.wrap_gate(gate)
if not hasattr(gate, "eigenvalues_magnitude"):
return QCircuit.wrap_gate(gate)
steps = 1 if not hasattr(gate, "steps") else gate.steps
return do_compile_trotterized_gate(generator=gate.generator, steps=steps, randomize=False,
factor=gate.parameter, control=gate.control)
[docs]
@compiler
def compile_trotterized_gate(gate, compile_exponential_pauli: bool = False):
"""
Parameters
----------
gate
compile_exponential_pauli
Returns
-------
"""
if not hasattr(gate, "steps") or hasattr(gate, "eigenvalues_magnitude"):
return QCircuit.wrap_gate(gate)
randomize=False
if hasattr(gate, "randomize"):
randomize=gate.randomize
result = do_compile_trotterized_gate(generator=gate.generator, steps=gate.steps, factor=gate.parameter, randomize=randomize, control=gate.control)
if compile_exponential_pauli:
return compile_exponential_pauli_gate(result)
else:
return result
[docs]
@compiler
def compile_ry(gate: RotationGateImpl, controlled_rotation: bool = False) -> QCircuit:
"""
Compile Ry gates into Rx and Rz.
Parameters
----------
gate:
the gate.
controlled_rotation:
determines if the decomposition of the controlled-Ry gate will be performed in compile_controlled_rotation,
if not, decomposition will be performed here
Returns
-------
QCircuit, the result of compilation.
"""
if gate.name.lower() == "ry":
if not (gate.is_controlled() and controlled_rotation):
return Rz(target=gate.target, control=None, angle=-numpy.pi / 2) \
+ Rx(target=gate.target, control=gate.control, angle=gate.parameter) \
+ Rz(target=gate.target, control=None, angle=numpy.pi / 2)
return QCircuit.wrap_gate(gate)
[docs]
@compiler
def compile_y(gate) -> QCircuit:
"""
Compile Y gates into X and Rz.
Parameters
----------
gate:
the gate.
Returns
-------
QCircuit, the result of compilation.
"""
if gate.name.lower() == "y":
return Rz(target=gate.target, control=None, angle=-numpy.pi / 2) \
+ X(target=gate.target, control=gate.control, power=gate.power if gate.is_parametrized() else None) \
+ Rz(target=gate.target, control=None, angle=numpy.pi / 2)
else:
return QCircuit.wrap_gate(gate)
[docs]
@compiler
def compile_ch(gate: QGateImpl) -> QCircuit:
"""
Compile CH gates into its equivalent:
CH = Ry(0.25pi) CZ Ry(-0.25pi)
Parameters
----------
gate:
the gate.
Returns
-------
QCircuit, the result of compilation.
"""
if gate.name.lower() == "h" and gate.is_controlled():
return Ry(target=gate.target, control=None, angle=-numpy.pi / 4) \
+ Z(target=gate.target, control=gate.control, power=gate.power if gate.is_parametrized() else None) \
+ Ry(target=gate.target, control=None, angle=numpy.pi / 4)
else:
return QCircuit.wrap_gate(gate)