Source code for tequila_code.circuit.gates

from tequila.circuit.circuit import QCircuit
from tequila.objective.objective import Variable, assign_variable
from tequila.circuit import _gates_impl as impl
import typing, numbers
from tequila.hamiltonian import PauliString, QubitHamiltonian, paulis
from tequila.tools import list_assignment
import numpy as np
import copy


[docs] def Phase(target: typing.Union[list, int], control: typing.Union[list, int] = None, angle: typing.Union[typing.Hashable, numbers.Number] = None, *args, **kwargs) -> QCircuit: """ Notes ---------- Initialize an abstract phase gate which acts as .. math:: S(\\phi) = \\begin{pmatrix} 1 & 0 \\\\ 0 & e^{i\\phi} \\end{pmatrix} Parameters ---------- angle defines the phase, can be numeric type (static gate) or hashable non-numeric type (parametrized gate) target int or list of int control int or list of int Returns ------- QCircuit object """ # ensure backward compatibility if "phi" in kwargs: if angle is None: angle = kwargs["phi"] else: raise Exception( "tq.gates.Phase initialization: You gave two angles angle={} and phi={}. Please only use angle".format( angle, kwargs["phi"])) if angle is None: angle = np.pi target = list_assignment(target) gates = [impl.PhaseGateImpl(phase=angle, target=q, control=control) for q in target] return QCircuit.wrap_gate(gates)
[docs] def S(target: typing.Union[list, int], control: typing.Union[list, int] = None) -> QCircuit: """ Notes ---------- .. math:: S = \\begin{pmatrix} 1 & 0 \\\\ 0 & e^{i\\frac{\\pi}{2}} \\end{pmatrix} Parameters ---------- target int or list of int control int or list of int Returns ------- QCircuit object """ return Phase(angle=np.pi / 2, target=target, control=control)
[docs] def T(target: typing.Union[list, int], control: typing.Union[list, int] = None): """ Notes ---------- Fixed phase gate .. math:: T = \\begin{pmatrix} 1 & 0 \\\\ 0 & e^{i\\frac{\\pi}{4}} \\end{pmatrix} Parameters ---------- target int or list of int control int or list of int Returns ------- QCircuit object """ return Phase(angle=np.pi / 4, target=target, control=control)
[docs] def Rx(angle, target: typing.Union[list, int], control: typing.Union[list, int] = None, assume_real=False) -> QCircuit: """ Notes ---------- Rx gate of the form .. math:: R_{x}(\\text{angle}) = e^{-i\\frac{\\text{angle}}{2} \\sigma_{x}} Parameters ---------- angle Hashable type (will be treated as Variable) or Numeric type (static angle) target integer or list of integers control integer or list of integers assume_real enable improved gradient compilation for controlled gates Returns ------- QCircuit object with this RotationGate """ return RotationGate(axis=0, angle=angle, target=target, control=control, assume_real=assume_real)
[docs] def Ry(angle, target: typing.Union[list, int], control: typing.Union[list, int] = None, assume_real=False) -> QCircuit: """ Notes ---------- Ry gate of the form .. math:: R_{y}(\\text{angle}) = e^{-i\\frac{\\text{angle}}{2} \\sigma_{y}} Parameters ---------- angle Hashable type (will be treated as Variable) or Numeric type (static angle) target integer or list of integers control integer or list of integers Returns ------- QCircuit object with this RotationGate """ return RotationGate(axis=1, angle=angle, target=target, control=control, assume_real=assume_real)
[docs] def Rz(angle, target: typing.Union[list, int], control: typing.Union[list, int] = None, assume_real=False) -> QCircuit: """ Notes ---------- Rz gate of the form .. math:: R_{z}(\\text{angle}) = e^{-i\\frac{\\text{angle}}{2} \\sigma_{z}} Parameters ---------- angle Hashable type (will be treated as Variable) or Numeric type (static angle) target integer or list of integers control integer or list of integers Returns QCircuit object with this RotationGate ------- """ return RotationGate(axis=2, angle=angle, target=target, control=control, assume_real=assume_real)
[docs] def X(target: typing.Union[list, int], control: typing.Union[list, int] = None, power=None, angle=None, *args, **kwargs) -> QCircuit: """ Notes ---------- Pauli X Gate Parameters ---------- target int or list of int control int or list of int power numeric type (fixed exponent) or hashable type (parametrized exponent) angle similar to power, but will be interpreted as .. math:: U(\\text{angle})=e^{-i\\frac{angle}{2} (1-X)} the default is angle=pi .. math:: U(\\pi) = X If angle and power are given both, tequila will combine them Returns ------- QCircuit object """ generator = lambda q: paulis.X(q) - paulis.I(q) return _initialize_power_gate(name="X", power=power, angle=angle, target=target, control=control, generator=generator, *args, **kwargs)
[docs] def H(target: typing.Union[list, int], control: typing.Union[list, int] = None, power=None, angle=None, *args, **kwargs) -> QCircuit: """ Notes ---------- Hadamard gate Parameters ---------- target int or list of int control int or list of int power numeric type (fixed exponent) or hashable type (parametrized exponent) angle similar to power, but will be interpreted as .. math:: U(\\text{angle})=e^{-i\\frac{angle}{2} generator} the default is angle=pi .. math:: U(\\pi) = H If angle and power are given both, tequila will combine them Returns ------- QCircuit object """ coef = 1 / np.sqrt(2) generator = lambda q: coef * (paulis.Z(q) + paulis.X(q)) - paulis.I(q) return _initialize_power_gate(name="H", power=power, angle=angle, target=target, control=control, generator=generator, *args, **kwargs)
[docs] def Y(target: typing.Union[list, int], control: typing.Union[list, int] = None, power=None, angle=None, *args, **kwargs) -> QCircuit: """ Notes ---------- Pauli Y Gate Parameters ---------- target: int or list of int control int or list of int power numeric type (fixed exponent) or hashable type (parametrized exponent) angle similar to power, but will be interpreted as .. math:: U(\\text{angle})=e^{-i\\frac{angle}{2} (1-Y)} the default is angle=pi .. math:: U(\\pi) = Y If angle and power are given both, tequila will combine them Returns ------- QCircuit object """ generator = lambda q: paulis.Y(q) - paulis.I(q) return _initialize_power_gate(name="Y", power=power, angle=angle, target=target, control=control, generator=generator)
[docs] def Z(target: typing.Union[list, int], control: typing.Union[list, int] = None, power=None, angle=None, *args, **kwargs) -> QCircuit: """ Notes ---------- Pauli Z Gate Parameters ---------- target int or list of int control int or list of int power numeric type (fixed exponent) or hashable type (parametrized exponent) angle similar to power, but will be interpreted as .. math:: U(\\text{angle})=e^{-i\\frac{angle}{2} (1-Z)} the default is angle=pi .. math:: U(\\pi) = Z If angle and power are given both, tequila will combine them Returns ------- QCircuit object """ generator = lambda q: paulis.Z(q) - paulis.I(q) return _initialize_power_gate(name="Z", power=power, angle=angle, target=target, control=control, generator=generator, *args, **kwargs)
[docs] def ExpPauli(paulistring: typing.Union[PauliString, str, dict], angle, control: typing.Union[list, int] = None, *args, **kwargs): """Exponentiated Pauligate: ExpPauli(PauliString, angle) = exp(-i* angle/2* PauliString) Parameters ---------- paulistring : given as PauliString structure or as string or dict or list if given as string: Format should be like X(0)Y(3)Z(2) if given as list: Format should be like [(0,'X'),(3,'Y'),(2,'Z')] if given as dict: Format should be like { 0:'X', 3:'Y', 2:'Z' } angle : the angle (will be multiplied by paulistring coefficient if there is one) control : control qubits paulistring: typing.Union[PauliString : str] : control: typing.Union[list : int] : (Default value = None) Returns ------- type Gate wrapped in circuit """ ps = _convert_Paulistring(paulistring) # Failsave: If the paulistring contains just one pauli matrix # it is better to initialize a rotational gate due to strange conventions in some simulators if len(ps.items()) == 1: target, axis = tuple(ps.items())[0] return QCircuit.wrap_gate( impl.RotationGateImpl(axis=axis, target=target, angle=ps.coeff * assign_variable(angle), control=control, *args, **kwargs)) else: return QCircuit.wrap_gate(impl.ExponentialPauliGateImpl(paulistring=ps, angle=angle, control=control, *args, **kwargs))
[docs] def Rp(paulistring: typing.Union[PauliString, str], angle, control: typing.Union[list, int] = None, *args, **kwargs): """ Same as ExpPauli """ return ExpPauli(paulistring=paulistring, angle=angle, control=control, *args, **kwargs)
[docs] def GenRot(*args, **kwargs): return GeneralizedRotation(*args, **kwargs)
[docs] 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, p0=None, steps: int = 1, assume_real=False) -> QCircuit: """ Notes -------- A gates which is shift-rule differentiable - its generator only has two distinguishable eigenvalues - it is then differentiable by the shift rule - eigenvalues_magnitude needs to be given upon initialization (this is "r" from Schuld et. al. and the default is r=1/2) - the generator will not (!) be verified to fullfill the properties Compiling will be done in analogy to a trotterized gate with steps=1 as default The gate will act in the same way as rotations and exppauli gates .. math:: U_{G}(\\text{angle}) = e^{-i\\frac{\\text{angle}}{2} G} Parameters ---------- angle numeric type or hashable symbol or tequila objective generator tequila QubitHamiltonian or any other structure with paulistrings control 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) Returns ------- The gate wrapped in a circuit """ return QCircuit.wrap_gate( impl.GeneralizedRotationImpl(angle=assign_variable(angle), generator=generator, control=control, eigenvalues_magnitude=eigenvalues_magnitude, steps=steps, assume_real=assume_real, p0=p0))
[docs] def Trotterized(generator: QubitHamiltonian = None, steps: int = 1, angle: typing.Union[typing.Hashable, numbers.Real, Variable] = None, control: typing.Union[list, int] = None, randomize=False, *args, **kwargs) -> QCircuit: """ Parameters ---------- generator : generator of the gate U = e^{-i\frac{angle}{2} G } angles : coefficients for each generator steps : trotter steps control : control qubits generators: QubitHamiltonian : The generator of the gate steps: int : Trotter Steps angle: typing.Hashable : A symbol that will be converted to a tq.Variable numbers.Real : A fixed real number Variable : A tequila Variable control: control qubits Returns ------- QCircuit """ # downward compatibility if "generators" in kwargs: if generator is None: if len(kwargs["generators"]) > 1: if "angles" not in kwargs: angles = [angle]*len(kwargs["generators"]) else: angles = kwargs["angles"] result = QCircuit() for angle,g in zip(angles,kwargs["generators"]): result += Trotterized(generator=g, angle=angle, steps=steps, control=control, randomize=randomize) return result else: generator = kwargs["generators"][0] else: raise Exception("Trotterized: You gave generators={} and generator={}".format(generator, kwargs["generators"])) if "angles" in kwargs: if angle is None: if len(kwargs["angles"]) > 1: raise Exception("multiple angles given, but only one generator") angle = kwargs["angles"][0] else: raise Exception("Trotterized: You gave angles={} and angle={}".format(angle, kwargs["angles"])) angle = assign_variable(angle) return QCircuit.wrap_gate(impl.TrotterizedGateImpl(generator=generator, angle=angle, steps=steps, control=control, randomize=randomize, **kwargs))
[docs] def SWAP(first: int, second: int, angle: float = None, control: typing.Union[int, list] = None, power: float = None, *args, **kwargs) -> QCircuit: """ Notes ---------- SWAP gate, order of targets does not matter Parameters ---------- first: int 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 in the form (SWAP)^n Returns ------- QCircuit """ 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 angle is None or power in [1, 1.0]: return QGate(name="SWAP", target=target, control=control, generator=generator) else: return GeneralizedRotation(angle=angle, control=control, generator=generator, eigenvalues_magnitude=0.25)
[docs] 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)
[docs] 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
""" Convenience Initialization Routines for controlled gates following the patern: Gate(control_qubit, target_qubit, possible_parameter) All can be initialized as well with the standard operations above """
[docs] def CNOT(control: int, target: int) -> QCircuit: """ Convenience CNOT initialization Parameters ---------- control: int control qubit target: int target qubit Returns ------- QCircuit object """ return X(target=target, control=control)
[docs] def Toffoli(first: int, second: int, target: int) -> QCircuit: """ Convenience Toffoli initialization Parameters ---------- first: int first control qubit second: int second control qubit target: int target qubit Returns ------- QCircuit object """ return X(target=target, control=[first, second])
[docs] def CX(control: int, target: int) -> QCircuit: """ Convenience initialization CX (CNOT) Parameters ---------- control: int control qubit target: int target qubit Returns ------- QCircuit object """ return X(target=target, control=control)
[docs] def CY(control: int, target: int) -> QCircuit: """ Convenience initialization CY (controlled Pauli Y) Parameters ---------- control: int control qubit target: int target qubit Returns ------- QCircuit object """ return Y(target=target, control=control)
[docs] def CZ(control: int, target: int) -> QCircuit: """ Convenience initialization CZ (controlled Pauli Z) Parameters ---------- control: int control qubit target: int target qubit Returns ------- QCircuit object """ return Z(target=target, control=control)
[docs] def CRx(control: int, target: int, angle: float) -> QCircuit: """ Convenience initialization CRx (controlled Pauli X Rotation) Parameters ---------- control: int control qubit target: int target qubit angle: Hashable type (will be treated as Variable) or Numeric type (static angle) Returns ------- QCircuit object """ return Rx(target=target, control=control, angle=angle)
[docs] def CRy(control: int, target: int, angle: float) -> QCircuit: """ Convenience initialization CRy (controlled Pauli Y Rotation) Parameters ---------- control: int control qubit target: int target qubit angle: Hashable type (will be treated as Variable) or Numeric type (static angle) Returns ------- QCircuit object """ return Ry(target=target, control=control, angle=angle)
[docs] def CRz(control: int, target: int, angle: float) -> QCircuit: """ Convenience initialization CRz (controlled Pauli Z Rotation) Parameters ---------- control: int control qubit target: int target qubit angle: Hashable type (will be treated as Variable) or Numeric type (static angle) Returns ------- QCircuit object """ return Rz(target=target, control=control, angle=angle)
[docs] def U(theta, phi, lambd, target: typing.Union[list, int], control: typing.Union[list, int] = None) -> QCircuit: """ Notes ---------- Convenient gate, one of the abstract gates defined by OpenQASM. .. math:: U(\\theta, \\phi, \\lambda) = R_z(\\phi)R_x(-\\pi/2)R_z(\\theta)R_x(\\pi/2)R_z(\\lambda) U(\\theta, \\phi, \\lambda) = \\begin{pmatrix} e^{-i \\frac{\\phi}{2}} & 0 \\\\ 0 & e^{i \\frac{\\phi}{2}} \\end{pmatrix} \\begin{pmatrix} \\cos{-\\frac{\\pi}{4}} & -i \\sin{-\\frac{\\pi}{4}} \\\\ -i \\sin{-\\frac{\\pi}{4}} & \\cos{-\\frac{\\pi}{4}} \\end{pmatrix} \\begin{pmatrix} e^{-i \\frac{\\theta}{2}} & 0 \\\\ 0 & e^{i \\frac{\\theta}{2}} \\end{pmatrix} \\begin{pmatrix} \\cos{\\frac{\\pi}{4}} & -i \\sin{\\frac{\\pi}{4}} \\\\ -i \\sin{\\frac{\\pi}{4}} & \\cos{\\frac{\\pi}{4}} \\end{pmatrix} \\begin{pmatrix} e^{-i \\frac{\\lambda}{2}} & 0 \\\\ 0 & e^{i \\frac{\\lambda}{2}} \\end{pmatrix} U(\\theta, \\phi, \\lambda) = \\begin{pmatrix} \\cos{\\frac{\\theta}{2}} & -e^{i \\lambda} \\sin{\\frac{\\theta}{2}} \\\\ e^{i \\phi} \\sin{\\frac{\\theta}{2}} & e^{i (\\phi+\\lambda)} \\cos{\\frac{\\theta}{2}} \\end{pmatrix} Parameters ---------- theta first parameter angle phi second parameter angle lamnd third parameter angle target int or list of int control int or list of int Returns ------- QCircuit object """ theta = assign_variable(theta) phi = assign_variable(phi) lambd = assign_variable(lambd) pi_half = assign_variable(np.pi / 2) return Rz(angle=lambd, target=target, control=control) + \ Rx(angle=pi_half, target=target, control=control) + \ Rz(angle=theta, target=target, control=control) + \ Rx(angle=-pi_half, target=target, control=control) + \ Rz(angle=phi, target=target, control=control)
[docs] def u1(lambd, target: typing.Union[list, int], control: typing.Union[list, int] = None) -> QCircuit: """ Notes ---------- Convenient gate, one of the abstract gates defined by Quantum Experience Standard Header. Changes the phase of a carrier without applying any pulses. .. math:: from OpenQASM 2.0 specification: u1(\\lambda) \\sim U(0, 0, \\lambda) = R_z(\\lambda) = e^{-i\\frac{\\lambda}{2} \\sigma_{z}} also is equal to: u1(\\lambda) = \\begin{pmatrix} 1 & 0 \\\\ 0 & e^{i\\lambda} \\end{pmatrix} which is the Tequila Phase gate: u1(\\lambda) = Phase(\\lambda) Parameters ---------- lambd parameter angle target int or list of int control int or list of int Returns ------- QCircuit object """ return Phase(phi=lambd, target=target, control=control)
[docs] def u2(phi, lambd, target: typing.Union[list, int], control: typing.Union[list, int] = None) -> QCircuit: """ Notes ---------- Convenient gate, one of the abstract gates defined by Quantum Experience Standard Header. Uses a single \\pi/2-pulse. .. math:: u2(\\phi, \\lambda) = U(\\pi/2, \\phi, \\lambda) = R_z(\\phi + \\pi/2)R_x(\\pi/2)R_z(\\lambda - \\pi/2) u2(\\phi, \\lambda) = \\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & -e^{i\\lambda} \\\\ e^{i\\phi} & e^{i(\\phi+\\lambda)} \\end{pmatrix} Parameters ---------- phi first parameter angle lambd second parameter angle target int or list of int control int or list of int Returns ------- QCircuit object """ return U(theta=np.pi / 2, phi=phi, lambd=lambd, target=target, control=control)
[docs] def u3(theta, phi, lambd, target: typing.Union[list, int], control: typing.Union[list, int] = None) -> QCircuit: """ Notes ---------- Convenient gate, one of the abstract gates defined by Quantum Experience Standard Header The most general single-qubit gate. Uses a pair of \\pi/2-pulses. .. math:: u3(\\theta, \\phi, \\lambda) = U(\\theta, \\phi, \\lambda) = \\begin{pmatrix} \\cos{\\frac{\\5theta}{2}} & -e^{i \\lambda} \\sin{\\frac{\\theta}{2}} \\\\ e^{i \\phi} \\sin{\\frac{\\theta}{2}} & e^{i (\\phi+\\lambda)} \\cos{\\frac{\\theta}{2}} \\end{pmatrix} Parameters ---------- theta first parameter angle phi second parameter angle lambd third parameter angle target int or list of int control int or list of int Returns ------- QCircuit object """ return U(theta=theta, phi=phi, lambd=lambd, target=target, control=control)
[docs] def QubitExcitation(angle: typing.Union[numbers.Real, Variable, typing.Hashable], target: typing.List, control=None, assume_real: bool = False, compile_options="optimize"): """ A Qubit Excitation, as described under "qubit perspective" in https://doi.org/10.1039/D0SC06627C For the Fermionic operators under corresponding Qubit encodings: Use the chemistry interface Parameters ---------- angle: the angle of the excitation unitary target: even number of qubit indices interpreted as [0,1,2,3....] = [(0,1), (2,3), ...] i.e. as qubit excitations from 0 to 1, 2 to 3, etc control: possible control qubits assume_real: assume the wavefunction on which this acts is always real (cheaper gradients: see https://doi.org/10.1039/D0SC06627C) Returns ------- QubitExcitation gate wrapped into a tequila circuit """ try: assert len(target) % 2 == 0 except: raise Exception("QubitExcitation: Needs an even number of targets") return QCircuit.wrap_gate(QubitExcitationImpl(angle=angle, target=target, assume_real=assume_real, compile_options=compile_options, control=control))
""" Helper Functions """ def _initialize_power_gate(name: str, target: typing.Union[list, int], generator, control: typing.Union[list, int] = None, power=None, angle=None, *args, **kwargs) -> QCircuit: target = list_assignment(target) # allow angle instead of power in initialization for more consistency # if angle is given we just convert it if angle is not None: angle = assign_variable(angle) if power is not None: power = power * angle / np.pi else: power = angle / np.pi if power is None or power in [1, 1.0]: gates = [impl.QGateImpl(name=name, target=q, control=control, generator=generator(q)) for q in target] else: gates = [impl.PowerGateImpl(name=name, power=power, target=q, control=control, generator=generator(q), *args, **kwargs) for q in target] return QCircuit.wrap_gate(gates)
[docs] def RotationGate(axis: int, angle: typing.Union[typing.Hashable, numbers.Number], target: typing.Union[list, int], control: typing.Union[list, int] = None, assume_real=False): """ Notes ---------- Initialize an abstract rotation gate of the form .. math:: R_{\\text{axis}}(\\text{angle}) = e^{-i\\frac{\\text{angle}}{2} \\sigma_{\\text{axis}}} Parameters ---------- axis integer 1 for x, 2 for y, 3 for z angle Hashable type (will be treated as Variable) or Numeric type (static angle) target integer or list of integers control integer or list of integers assume_real enable improved gradient compilation for controlled gates (wavefunction needs to be real) Returns ------- QCircuit object with this RotationGate """ target = list_assignment(target) gates = [impl.RotationGateImpl(axis=axis, angle=angle, target=q, control=control, assume_real=assume_real) for q in target] return QCircuit.wrap_gate(gates)
[docs] def PowerGate(name: str, target: typing.Union[list, int], power: float = None, control: typing.Union[list, int] = None, generator: QubitHamiltonian = None, *args, **kwargs): """ Initialize a (potentially parametrized) gate which is supported on the backend Parameters ---------- name: str name of the gate on the backend (usually, H, X, Y, Z) target int or list of int power numeric type (fixed exponent) or hashable type (parametrized exponent) will be interpreted as angle similar to power, but will be interpreted as .. math:: U=e^{-i\\frac{angle}{2} generator} control int or list of int Returns ------- """ return QCircuit.wrap_gate( impl.PowerGateImpl(name=name, power=power, target=target, control=control, generator=generator, *args, **kwargs))
[docs] def QGate(name, target: typing.Union[list, int], control: typing.Union[list, int] = None, generator: QubitHamiltonian = None): return QCircuit.wrap_gate(impl.QGateImpl(name=name, target=target, control=control, generator=generator))
""" Implementation of specific gates Not put into _gates_impl.py for convenience Those gate types will not be recognized by the compiler and should all implement a compile function that returns a QCircuit of primitive tq gates """
[docs] 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) if generator is None: assert target is not None assert p0 is None generator = paulis.I() p0a = paulis.I() p0b = paulis.I() for i in range(len(target) // 2): generator *= paulis.Sp(target[2 * i]) * paulis.Sm(target[2 * i + 1]) p0a *= paulis.Qp(target[2 * i]) * paulis.Qm(target[2 * i + 1]) p0b *= paulis.Qm(target[2 * i]) * paulis.Qp(target[2 * i + 1]) generator = (1.0j * (generator - generator.dagger())).simplify() p0 = paulis.I() - p0a - p0b else: assert generator is not None assert p0 is not None 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"): self.compile_options = compile_options.lower() else: self.compile_options = compile_options
[docs] def map_qubits(self, qubit_map: dict): mapped = super().map_qubits(qubit_map) mapped.p0 = self.p0.map_qubits(qubit_map=qubit_map) return mapped
[docs] def compile(self, exponential_pauli=False, *args, **kwargs): # optimized compiling for single and double qubit excitaitons following arxiv:2005.14475 # Alternative representation in arxiv:2104.05695 (not implemented -> could be added and controlled with optional compile keywords) if self.is_controlled(): control = list(self.control) else: control = [] if self.compile_options == "optimize" and len(self.target) == 2 and exponential_pauli: p,q = self.target U0 = X(target=p, control=q) U1 = Ry(angle=self.parameter, target=q, control=[p]+control) return U0 + U1 + U0 elif self.compile_options == "optimize" and len(self.target) == 4 and exponential_pauli: p,r,q,s = self.target U0 = X(target=q, control=p) U0 += X(target=s, control=r) U0 += X(target=r, control=p) U0 += X(target=q) U0 += X(target=s) U1 = Ry(angle=-self.parameter, target=p, control=[q,r,s]+control) return U0 + U1 + U0.dagger() else: return Trotterized(angle=self.parameter, generator=self.generator, steps=1, control=self.control)
def _convert_Paulistring(paulistring: typing.Union[PauliString, str, dict]) -> PauliString: ''' Function that given a paulistring as PauliString structure or as string or dict or list, returns the corresponding PauliString structure. Parameters ---------- paulistring : typing.Union[PauliString , str, dict] given as PauliString structure or as string or dict or list if given as string: Format should be like X(0)Y(3)Z(2) if given as list: Format should be like [(0,'X'),(3,'Y'),(2,'Z')] if given as dict: Format should be like { 0:'X', 3:'Y', 2:'Z' } Returns ------- ps : PauliString ''' if isinstance(paulistring, str): ps = PauliString.from_string(string=paulistring) elif isinstance(paulistring, list): ps = PauliString.from_openfermion(key=paulistring) elif isinstance(paulistring, dict): ps = PauliString(data=paulistring) else: ps = paulistring return ps
[docs] def PauliGate(paulistring: typing.Union[PauliString, str, dict], control: typing.Union[list, int] = None, *args, **kwargs) -> QCircuit: ''' Functions that converts a Pauli string into the corresponding quantum circuit. Parameters ---------- paulistring : typing.Union[PauliString , str, dict] given as PauliString structure or as string or dict or list if given as string: Format should be like X(0)Y(3)Z(2) if given as list: Format should be like [(0,'X'),(3,'Y'),(2,'Z')] if given as dict: Format should be like { 0:'X', 3:'Y', 2:'Z' } control: typing.Union[list, int] : (Default value = None) control qubits Raises ------ Exception: Not a Pauli Operator. Returns ------- U : QCircuit object corresponding to the Pauli string. ''' ps = _convert_Paulistring(paulistring) U = QCircuit() for k,v in ps.items(): if v.lower() == "x": U += X(target=k, control=control, *args, **kwargs) elif v.lower() == "y": U += Y(target=k, control=control, *args, **kwargs) elif v.lower() == "z": U += Z(target=k, control=control, *args, **kwargs) else: raise Exception("{}???".format(v)) return U