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 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_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 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) if isinstance(gate, VectorObjective): return type(gate)(argsets=outer, transformations=gate._transformations) else: return f(gate=gate, **kwargs)
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_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_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 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, 'transformation'): compiled = [] for E in gate.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)) return type(gate)(args=compiled, transformation=gate._transformation) else: return f(gate=gate, **kwargs)
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 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_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 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 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_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 hadamard_recursor(gate) -> QCircuit: if not isinstance(gate, PowerGateImpl) or gate.name not in [ 'H', 'h', 'hadamard' ]: return QCircuit.wrap_gate(gate) result = QCircuit() cl = 0 if gate.is_controlled(): cl = len(gate.control) if cl == 0: return hadamard_base(gate) if cl == 1: return hadamard_axbxc(gate) if cl == 2: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[1]) result += hadamard_axbxc(v) result += CNOT(gate.control[0], gate.control[1]) vdag = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[1]).dagger() result += hadamard_axbxc(vdag) result += CNOT(gate.control[0], gate.control[1]) again = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[0]) result += hadamard_axbxc(again) else: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[-1]) result += hadamard_axbxc(v) result += CNOT(target=gate.control[cl - 1], control=gate.control[0:cl - 1]) vdag = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[-1]).dagger() result += hadamard_axbxc(vdag) result += CNOT(target=gate.control[cl - 1], control=gate.control[0:cl - 1]) rebuild = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[:cl - 1]) result += hadamard_recursor(rebuild) return result
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_phase(gate) -> QCircuit: 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 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 change_basis(target, axis, 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 isinstance(axis, str): axis = RotationGateImpl.string_to_axis[axis.lower()] if axis == 0: return H(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()
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 __init__(self, abstract_circuit: QCircuit, variables, noise_model=None, use_mapping=True, optimize_circuit=True, *args, **kwargs): self._variables = tuple(abstract_circuit.extract_variables()) self.use_mapping = use_mapping compiler_arguments = self.compiler_arguments if noise_model is not None: compiler_arguments["cc_max"] = True compiler_arguments["controlled_phase"] = True compiler_arguments["controlled_rotation"] = True compiler_arguments["hadamard_power"] = True # compile the abstract_circuit c = compiler.Compiler(**compiler_arguments) if self.use_mapping: qubits = abstract_circuit.qubits else: qubits = range(abstract_circuit.n_qubits) self._qubits = qubits self.abstract_qubit_map = {q: i for i, q in enumerate(qubits)} self.qubit_map = self.make_qubit_map(qubits) compiled = c(abstract_circuit) self.abstract_circuit = compiled # translate into the backend object self.circuit = self.create_circuit(abstract_circuit=compiled, variables=variables) if optimize_circuit: self.circuit = self.optimize_circuit(circuit=self.circuit) self.noise_model = noise_model
def do_compile_trotterized_gate(generator, steps, factor, randomize, control): """ Todo: Jakob, plz write """ 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
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 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 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 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 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 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 _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)