def compile_to_cc(gate) -> QCircuit: if not gate.is_controlled: return QCircuit.wrap_gate(gate) cl = len(gate.control) target = gate.target control = gate.control if cl <= 2: 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) back += compile_power_gate(gate=new, cut=True) elif isinstance(gate, RotationGateImpl): partial = compile_controlled_rotation(gate=gate) back += compile_to_cc(gate=partial) elif isinstance(gate, PhaseGateImpl): partial = compile_controlled_phase(gate=gate) back += compile_to_cc(gate=partial) else: print(gate) raise TequilaException('frankly, what the f**k is this gate?') return back
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
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)
def compile_controlled_rotation(gate: RotationGateImpl, angles: list = None) -> 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 :param angles: new angles to set, given as a list of two. If None the angle in the gate is used (default) :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 angles is None: angles = [gate.parameter / 2, -gate.parameter / 2] if len(gate.target) > 1: return compile_controlled_rotation(gate=compile_multitarget(gate=gate), angles=angles) target = gate.target control = gate.control result = QCircuit() result += change_basis(target=target, axis=gate._axis) result += RotationGateImpl(axis="z", target=target, angle=angles[0]) result += QGateImpl(name="X", target=target, control=control) result += RotationGateImpl(axis="Z", target=target, angle=angles[1]) result += QGateImpl(name="X", target=target, control=control) result += change_basis(target=target, axis=gate._axis, daggered=True) result.n_qubits = result.max_qubit() + 1 return result
def compile_controlled_phase(gate) -> QCircuit: if not isinstance(gate, PhaseGateImpl): return QCircuit.wrap_gate(gate) if len(gate.control) == 0: return QCircuit.wrap_gate(gate) count = len(gate.control) result = QCircuit() phase = gate.parameter if count == 1: result += H(target=gate.target) result += CNOT(gate.control, gate.target) result += H(target=gate.target) result += Phase(gate.parameter + numpy.pi, target=gate.target) elif count == 2: result += Rz(angle=phase / (2**2), target=gate.control[0]) result += Rz(angle=phase / (2**(1)), target=gate.control[1], control=gate.control[0]) result += Rz(angle=phase, target=gate.target, control=gate.control) elif count >= 3: result += Rz(angle=phase / (2**count), target=gate.control[0]) for i in range(1, count): result += Rz(angle=phase / (2**(count - i)), target=gate.control[i], control=gate.control[0:i]) result += Rz(angle=phase, target=gate.target, control=gate.control) return result
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
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)
def compile_multitarget(gate, variables=None) -> QCircuit: 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 isinstance(gate, MeasurementImpl): 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
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
def compile_power_gate(gate, cut=False) -> QCircuit: if not isinstance(gate, PowerGateImpl): return QCircuit.wrap_gate(gate) if gate.name.lower() in ['h', 'hadamard']: return QCircuit.wrap_gate(gate=gate) if not gate.is_controlled(): return compile_power_base(gate=gate) return power_recursor(gate=gate, cut=cut)
def ExpPauli(paulistring: typing.Union[PauliString, str], angle, control: typing.Union[list, int] = None): """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 """ if isinstance(paulistring, str): ps = PauliString.from_string(string=paulistring) elif isinstance(paulistring, list): ps = PauliString.from_openfermion(key=list) elif isinstance(paulistring, dict): ps = PauliString(data=paulistring) else: ps = 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)) else: return QCircuit.wrap_gate( impl.ExponentialPauliGateImpl(paulistring=ps, angle=angle, control=control))
def compile_gaussian_gate(gate, compile_exponential_pauli: bool = False): if not hasattr(gate, "generator"): return QCircuit.wrap_gate(gate) if not hasattr(gate, "shift"): return QCircuit.wrap_gate(gate) return do_compile_trotterized_gate(generator=gate.generator, steps=gate.steps, randomize=False, factor=gate.parameter, control=gate.control)
def _initialize_power_gate(name: str, target: typing.Union[list, int], control: typing.Union[list, int] = None, power=None) -> QCircuit: if power is None or power in [1, 1.0]: return QCircuit.wrap_gate( QGateImpl(name=name, target=target, control=control)) else: return QCircuit.wrap_gate( PowerGateImpl(name=name, power=power, target=target, control=control))
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)
def compile_trotterized_gate(gate, compile_exponential_pauli: bool = False): if not hasattr(gate, "generators") or not hasattr(gate, "steps"): return QCircuit.wrap_gate(gate) c = 1.0 result = QCircuit() if gate.join_components: for step in range(gate.steps): if gate.randomize_component_order: numpy.random.shuffle(gate.generators) for i, g in enumerate(gate.generators): if gate.angles is not None: c = gate.angles[i] result += do_compile_trotterized_gate(generator=g, steps=1, factor=c / gate.steps, randomize=gate.randomize, control=gate.control) else: if gate.randomize_component_order: numpy.random.shuffle(gate.generators) for i, g in enumerate(gate.generators): if gate.angles is not None: c = gate.angles[i] result += do_compile_trotterized_gate(generator=g, steps=gate.steps, factor=c, randomize=gate.randomize, control=gate.control) if compile_exponential_pauli: return compile_exponential_pauli_gate(result) else: return result
def compile_toffoli(gate) -> QCircuit: 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)
def RotationGate(axis: int, angle: typing.Union[typing.Hashable, numbers.Number], target: typing.Union[list, int], control: typing.Union[list, int] = None): """ 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 Returns ------- QCircuit object with this RotationGate """ return QCircuit.wrap_gate( RotationGateImpl(axis=axis, angle=angle, target=target, control=control))
def Rz(angle, target: typing.Union[list, int], control: typing.Union[list, int] = None) -> 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 QCircuit.wrap_gate( RotationGateImpl(axis=2, angle=angle, target=target, control=control))
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)
def PowerGate(name: str, target: typing.Union[list, int], power: float = None, angle: float = None, control: typing.Union[list, int] = None, generator: QubitHamiltonian = None): """ 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))
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") if hasattr(gate, "power") and gate.parameter != 1: raise TequilaCompilerException( "SWAP gate with power can not be compiled into CNOTS") c = [] if gate.control is not None: c = gate.control return X(target=gate.target[0], control=gate.target[1] + c) \ + X(target=gate.target[1], control=gate.target[0] + c) \ + X(target=gate.target[0], control=gate.target[1] + c) else: return QCircuit.wrap_gate(gate)
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
def Phase(phi: typing.Union[typing.Hashable, numbers.Number], target: typing.Union[list, int], control: typing.Union[list, int] = None) -> QCircuit: """ Notes ---------- Initialize an abstract phase gate which acts as .. math:: S(\\phi) = \\begin{pmatrix} 1 & 0 \\\\ 0 & e^{i\\phi} \\end{pmatrix} Parameters ---------- phi 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 """ return QCircuit.wrap_gate( PhaseGateImpl(phase=phi, target=target, control=control))
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) if len(gate.control) == 1: result += Rz(angle=phase / 2, target=gate.control, control=None) result += Rz(angle=phase, target=gate.target, control=gate.control) return result else: return compile_controlled_phase(gate)
def _initialize_power_gate(name: str, target: typing.Union[list, int], generator, control: typing.Union[list, int] = None, power=None, angle=None) -> QCircuit: target = list_assignment(target) 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)) for q in target ] return QCircuit.wrap_gate(gates)
def hadamard_axbxc(gate) -> QCircuit: """ Decompose 1 control parametrized hadamard into single qubit rotation and CNOT. Parameters ---------- gate: the gate Returns ------- QCircuit, the result of compilation. """ if not isinstance(gate, PowerGateImpl) or gate.name not in [ 'H', 'h', 'hadamard' ]: return QCircuit.wrap_gate(gate) power = gate.parameter target = gate.target a = power.wrap(a_calc) b = power.wrap(b_calc) theta = power.wrap(theta_calc) phase = power * jnp.pi / 2 result = QCircuit() result += Rz((a - b) / 2, target) result += CNOT(gate.control, target) result += Rz(-(a + b) / 2, target) result += Ry(-theta / 2, target) result += CNOT(gate.control, target) result += Ry(theta / 2, target) result += Rz(a, target) result += Phase(numpy.pi * power / 2, gate.control) return result
def hadamard_base(gate) -> QCircuit: """ base case for hadamard compilation; returns powers of hadamard as sequence of single qubit rotations. Parameters ---------- gate: the gate. Returns ------- A QCircuit; the result of compilation. """ if not isinstance(gate, PowerGateImpl) or gate.name not in [ 'H', 'h', 'hadamard' ]: return QCircuit.wrap_gate(gate) power = gate.parameter a = power.wrap(a_calc) b = power.wrap(b_calc) theta = power.wrap(theta_calc) result = QCircuit() result += Rz(angle=b, target=gate.target) result += Ry(angle=theta, target=gate.target) result += Rz(angle=a, target=gate.target) return result
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)
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))
def Trotterized(generators: typing.List[QubitHamiltonian], steps: int, angles: typing.Union[ typing.List[typing.Hashable], typing.List[numbers.Real], typing.List[Variable]] = None, control: typing.Union[list, int] = None, parameters: TrotterParameters = None) -> QCircuit: """ Parameters ---------- generators : list of generators angles : coefficients for each generator steps : trotter steps control : control qubits parameters : Additional Trotter parameters, if None then defaults are used generators: typing.List[QubitHamiltonian] : steps: int : angles: typing.Union[typing.List[typing.Hashable] : typing.List[numbers.Real] : typing.List[Variable]] : (Default value = None) control: typing.Union[list : int] : (Default value = None) parameters: TrotterParameters : (Default value = None) Returns ------- QCircuit """ # convenience if not (isinstance(generators, list) or isinstance(generators, tuple)): generators = [generators] if not (isinstance(angles, list) or isinstance(angles, tuple)): angles = [angles] if parameters is None: parameters = TrotterParameters() assigned_angles = [assign_variable(angle) for angle in angles] return QCircuit.wrap_gate( TrotterizedGateImpl(generators=generators, angles=assigned_angles, steps=steps, control=control, **parameters.__dict__))