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_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_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_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 test_moments(): c = QCircuit() c += CNOT(target=0, control=(1, 2, 3)) c += H(target=[0, 1]) c += Rx(angle=numpy.pi, target=[0, 3]) c += Z(target=1) c += Phase(phi=numpy.pi, target=4) moms = c.moments assert len(moms) == 3 assert (moms[0].gates[1].parameter == assign_variable(numpy.pi)) assert (moms[0].gates[1].target == (4, ))
def create_sub_circ(self, gate, control_bit, target_bit): sub_circ = None if gate == "CNOT": sub_circ = CNOT(control=control_bit, target=target_bit) elif gate == "aCNOT": sub_circ = X(control_bit) sub_circ += CNOT(control=control_bit, target=target_bit) sub_circ += X(control_bit) elif gate == "CROT": a_angle = self.coefficients[self.c_i] sa = sympy.Symbol(a_angle) self.c_i += 1 sub_circ = Ry(control=control_bit, target=target_bit, angle=SympyVariable(sa)) elif gate == "aCROT": a_angle = self.coefficients[self.c_i] sa = sympy.Symbol(a_angle) self.c_i += 1 sub_circ = X(control_bit) sub_circ += Ry(control=control_bit, target=target_bit, angle=SympyVariable(sa)) sub_circ += X(control_bit) elif gate == "X": sub_circ = X(target_bit) elif gate == "ROT": a_angle = self.coefficients[self.c_i] self.c_i += 1 sub_circ = Ry(control=None, target=target_bit, angle=SympyVariable(sympy.Symbol(a_angle))) return sub_circ
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 test_circuit_from_moments(): c = QCircuit() c += CNOT(target=0, control=(1, 2, 3)) c += Phase(phi=numpy.pi, target=4) c += Rx(angle=Variable('a'), target=[0, 3]) c += H(target=[0, 1]) c += Rx(angle=Variable('a'), target=[2, 3]) ## table[1] should equal 1 at this point, len(moments should be 3) c += Z(target=1) c += Rx(angle=Variable('a'), target=[0, 3]) moms = c.moments c2 = QCircuit.from_moments(moms) assert c == c2
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 test_canonical_moments(): c = QCircuit() c += CNOT(target=0, control=(1, 2, 3)) c += Rx(angle=Variable('a'), target=[0, 3]) c += H(target=[0, 1]) c += Rx(angle=Variable('a'), target=[2, 3]) c += Rx(angle=Variable('a'), target=[0, 3]) c += Z(target=1) c += Phase(phi=numpy.pi, target=4) moms = c.canonical_moments assert len(moms) == 6 assert (moms[0].gates[1].parameter == assign_variable(numpy.pi)) assert (moms[0].gates[1].target == (4, )) assert hasattr(moms[3].gates[0], 'axis') assert len(moms[0].qubits) == 5
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 compile_toffoli(gate) -> QCircuit: """ break down a toffoli gate into a sequence of CNOT and single qubit gates. Parameters ---------- gate: the gate. Returns ------- A QCircuit; the result of compilation. """ if gate.name.lower != 'x': return QCircuit.wrap_gate(gate) control = gate.control c1 = control[1] c0 = control[0] target = gate.target result = QCircuit() result += H(target) result += CNOT(c1, target) result += T(target).dagger() result += CNOT(c0, target) result += T(target) result += CNOT(c1, target) result += T(target).dagger() result += CNOT(c0, target) result += T(c1) result += T(target) result += CNOT(c0, c1) result += H(target) result += T(c0) result += T(c1).dagger() result += CNOT(c0, c1) return (result)
def hadamard_recursor(gate) -> QCircuit: """ recursive function for decomposing parametrized hadamard, potentially with controls. 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) 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 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
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 power_recursor(gate, cut=False) -> QCircuit: """ recursive function for decomposing parametrized, possibly controlled, power gates. Parameters ---------- gate: the gate. cut: bool: whether or not to stop recursion at 2 controls maximum. Default: False. Returns ------- A QCircuit; the result of compilation. """ result = QCircuit() cl = 0 if gate.is_controlled(): cl = len(gate.control) if cl == 0: return compile_power_base(gate=gate) elif cl == 1: return get_axbxc_decomp(gate=gate) elif cl == 2 and not cut: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[1]) result += get_axbxc_decomp(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 += get_axbxc_decomp(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 += get_axbxc_decomp(again) elif cl == 2 and cut: if gate.name in ['CCx', 'CCNOT', 'CCX', 'X']: return QCircuit.wrap_gate(gate) else: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[1]) result += get_axbxc_decomp(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 += get_axbxc_decomp(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 += get_axbxc_decomp(again) else: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[-1]) result += get_axbxc_decomp(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 += get_axbxc_decomp(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 += power_recursor(gate=rebuild, cut=cut) return result
def power_recursor(gate, cut=False) -> QCircuit: ''' if not hasattr(gate,'power'): return QCircuit.wrap_gate(gate) ''' result = QCircuit() cl = 0 if gate.is_controlled(): cl = len(gate.control) if cl == 0: return compile_power_base(gate=gate) elif cl == 1: return get_axbxc_decomp(gate=gate) elif cl == 2 and not cut: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[1]) result += get_axbxc_decomp(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 += get_axbxc_decomp(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 += get_axbxc_decomp(again) elif cl == 2 and cut: if gate.name in ['CCx', 'CCNOT', 'CCX', 'X']: return QCircuit.wrap_gate(gate) else: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[1]) result += get_axbxc_decomp(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 += get_axbxc_decomp(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 += get_axbxc_decomp(again) else: v = type(gate)(name=gate.name, power=gate.parameter / 2, target=gate.target, control=gate.control[-1]) result += get_axbxc_decomp(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 += get_axbxc_decomp(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 += power_recursor(gate=rebuild, cut=cut) return result