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 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 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 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_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_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 test_basic_gates(): I = sympy.I cos = sympy.cos sin = sympy.sin exp = sympy.exp BS = QubitWaveFunction.from_int angle = sympy.pi gates = [ X(0), Y(0), Z(0), Rx(target=0, angle=angle), Ry(target=0, angle=angle), Rz(target=0, angle=angle), H(0) ] results = [ BS(1), I * BS(1), BS(0), cos(-angle / 2) * BS(0) + I * sin(-angle / 2) * BS(1), cos(-angle / 2) * BS(0) + I * sin(-angle / 2) * I * BS(1), exp(-I * angle / 2) * BS(0), 1 / sympy.sqrt(2) * (BS(0) + BS(1)) ] for i, g in enumerate(gates): wfn = simulate(g, backend="symbolic", variables={angle: sympy.pi}) assert (wfn == strip_sympy_zeros(results[i]))
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_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_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_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 hadamard_base(gate) -> QCircuit: 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 test_unitary_gate_u2(ctrl, phi, lambd): """ Test some equivalences for u2 gate Since u2(\\phi, \\lambda) = Rz(\\phi)Ry(\\pi/2)Rz(\\lambda) """ c_u2 = u2(phi=phi, lambd=lambd, target=0, control=ctrl) c_equiv = Rz(target=0, control=ctrl, angle=lambd) + \ Ry(target=0, control=ctrl, angle=numpy.pi / 2) + \ Rz(target=0, control=ctrl, angle=phi) if ctrl is not None: c_u2 = X(target=ctrl) + c_u2 c_equiv = X(target=ctrl) + c_equiv wfn1 = simulate(c_u2, backend="symbolic") wfn2 = simulate(c_equiv, backend="symbolic") assert (numpy.isclose(wfn1.inner(wfn2), 1.0))
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)
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_axbxc(gate) -> QCircuit: 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_power_base(gate): if not isinstance(gate, PowerGateImpl): return QCircuit.wrap_gate(gate) power = gate.parameter if gate.name in ['H', 'h', 'Hadamard', 'hadamard']: return compile_h_power(gate=gate) if 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 test_consistency(): angle = numpy.pi / 2 cpairs = [(CNOT(target=0, control=1), X(target=0, control=1)), (Ry(target=0, angle=numpy.pi), Rz(target=0, angle=4 * numpy.pi) + X(target=0)), (Rz(target=0, angle=numpy.pi), Rz(target=0, angle=numpy.pi) + Z(target=0)), (Rz(target=0, angle=angle), Rz(target=0, angle=angle / 2) + Rz(target=0, angle=angle / 2)), (Rx(target=0, angle=angle), Rx(target=0, angle=angle / 2) + Rx(target=0, angle=angle / 2)), (Ry(target=0, angle=angle), Ry(target=0, angle=angle / 2) + Ry(target=0, angle=angle / 2))] for c in cpairs: print("circuit=", c[0], "\n", c[1]) wfn1 = simulate(c[0], backend="symbolic") wfn2 = simulate(c[1], backend="symbolic") assert (numpy.isclose(wfn1.inner(wfn2), 1.0))
def get_axbxc_decomp(gate): """ Break down single controlled parametrized power gates into CNOT and rotations. Parameters ---------- gate: the gate. Returns ------- QCircuit; the result of compilation. """ if not isinstance(gate, PowerGateImpl) or gate.name not in ['X', 'Y', 'Z']: return QCircuit.wrap_gate(gate) power = gate.parameter target = gate.target result = QCircuit() if gate.name == 'X': a = -numpy.pi / 2 b = numpy.pi / 2 theta = power * numpy.pi ''' result+=Phase(numpy.pi*power/2,gate.control) 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=target) ''' ''' 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) ''' result += Rx(angle=theta, target=target, control=gate.control) result += Phase(numpy.pi * power / 2, gate.control) elif gate.name == 'Y': ### off by global phase of Exp[ pi power /2] theta = power * numpy.pi ''' result+=Phase(numpy.pi*power/2,gate.control) result+=CNOT(gate.control,target) result+=Ry(-theta/2,target) result+=CNOT(gate.control,target) result+=Ry(theta/2,target) ''' a = 0 b = 0 # 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) elif gate.name == 'Z': a = 0 b = power * numpy.pi theta = 0 result += Rz(b / 2, target) result += CNOT(gate.control, target) result += Rz(-b / 2, target) result += CNOT(gate.control, target) # result+=Rz(a,target) result += Phase(numpy.pi * power / 2, gate.control) ''' result+=Rz(b/2,target) result+=CNOT(gate.control,target) result+=Rz(-b/2,target) result+=CNOT(gate.control,target) ''' return result
def get_axbxc_decomp(gate): if not isinstance(gate, PowerGateImpl) or gate.name not in ['X', 'Y', 'Z']: return QCircuit.wrap_gate(gate) power = gate.parameter target = gate.target result = QCircuit() if gate.name == 'X': a = -numpy.pi / 2 b = numpy.pi / 2 theta = power * numpy.pi ''' result+=Phase(numpy.pi*power/2,gate.control) 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=target) ''' ''' 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) ''' result += Rx(angle=theta, target=target, control=gate.control) result += Phase(numpy.pi * power / 2, gate.control) elif gate.name == 'Y': ### off by global phase of Exp[ pi power /2] theta = power * numpy.pi ''' result+=Phase(numpy.pi*power/2,gate.control) result+=CNOT(gate.control,target) result+=Ry(-theta/2,target) result+=CNOT(gate.control,target) result+=Ry(theta/2,target) ''' a = 0 b = 0 # 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) elif gate.name == 'Z': a = 0 b = power * numpy.pi theta = 0 result += Rz(b / 2, target) result += CNOT(gate.control, target) result += Rz(-b / 2, target) result += CNOT(gate.control, target) # result+=Rz(a,target) result += Phase(numpy.pi * power / 2, gate.control) ''' result+=Rz(b/2,target) result+=CNOT(gate.control,target) result+=Rz(-b/2,target) result+=CNOT(gate.control,target) ''' return result
"gate, theta, phi, lambd", [ (Rx(target=0, control=None, angle=numpy.pi / 5), numpy.pi / 5, -numpy.pi / 2, numpy.pi / 2), # Rx(angle) = u3(angle, -pi/2, pi/2) (Rx(target=0, control=1, angle=numpy.pi / 6), numpy.pi / 6, -numpy.pi / 2, numpy.pi / 2), (Rx(target=0, control=None, angle=numpy.pi / 7), numpy.pi / 7, -numpy.pi / 2, numpy.pi / 2), (Rx(target=0, control=1, angle=numpy.pi / 8), numpy.pi / 8, -numpy.pi / 2, numpy.pi / 2), (Ry(target=0, control=1, angle=numpy.pi / 4), numpy.pi / 4, 0, 0), # Ry(angle) = u3(angle, 0, 0) (Ry(target=0, control=1, angle=numpy.pi / 5), numpy.pi / 5, 0, 0), (Ry(target=0, control=1, angle=numpy.pi / 3), numpy.pi / 3, 0, 0), (Ry(target=0, control=1, angle=numpy.pi / 2), numpy.pi / 2, 0, 0), (Rz(target=0, control=None, angle=numpy.pi), 0, 0, numpy.pi), # Rz(angle) = U(0, 0, angle) (Rz(target=0, control=1, angle=numpy.pi / 6), 0, 0, numpy.pi / 6), (Rz(target=0, control=None, angle=numpy.pi / 7), 0, 0, numpy.pi / 7), (Rz(target=0, control=1, angle=numpy.pi / 8), 0, 0, numpy.pi / 8) ]) def test_unitary_gate_u_u3(gate, theta, phi, lambd): """ Test some equivalences for u3 gate (also U gate, because U = u3) """ c_u3 = u3(theta=theta, phi=phi, lambd=lambd, target=gate.gates[0].target, control=None if len(gate.gates[0].control) == 0 else gate.gates[0].control)