def test_classical() -> None: # circuit to cover capabilities covered in HQS example notebook c = Circuit(1) a = c.add_c_register("a", 8) b = c.add_c_register("b", 10) c.add_c_register("c", 10) c.add_c_setbits([True], [a[0]]) c.add_c_setbits([False, True] + [False] * 6, list(a)) c.add_c_setbits([True, True] + [False] * 8, list(b)) c.X(0, condition=reg_eq(a ^ b, 1)) c.X(0, condition=(a[0] ^ b[0])) c.X(0, condition=reg_eq(a & b, 1)) c.X(0, condition=reg_eq(a | b, 1)) c.X(0, condition=a[0]) c.X(0, condition=reg_neq(a, 1)) c.X(0, condition=if_not_bit(a[0])) c.X(0, condition=reg_gt(a, 1)) c.X(0, condition=reg_lt(a, 1)) c.X(0, condition=reg_geq(a, 1)) c.X(0, condition=reg_leq(a, 1)) b = HoneywellBackend("HQS-LT-S1-APIVAL") b.compile_circuit(c) assert b.get_counts(c, 10)
def test_conditions() -> None: box_c = Circuit(2, 2) box_c.Z(0) box_c.Y(1, condition_bits=[0, 1], condition_value=1) box_c.Measure(0, 0, condition_bits=[0, 1], condition_value=0) box = CircBox(box_c) u = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) ubox = Unitary2qBox(u) c = Circuit(2, 2, name="c") b = c.add_c_register("b", 1) c.add_circbox( box, [Qubit(0), Qubit(1), Bit(0), Bit(1)], condition_bits=[b[0]], condition_value=1, ) c.add_unitary2qbox(ubox, Qubit(0), Qubit(1), condition_bits=[b[0]], condition_value=0) c2 = c.copy() qc = tk_to_qiskit(c) c1 = qiskit_to_tk(qc) assert len(c1.get_commands()) == 2 DecomposeBoxes().apply(c) DecomposeBoxes().apply(c1) assert c == c1 c2.Z(1, condition=reg_eq(b, 1)) qc = tk_to_qiskit(c2) c1 = qiskit_to_tk(qc) assert len(c1.get_commands()) == 3
def test_mixed_circuit() -> None: c = Circuit() qr = c.add_q_register("q", 2) ar = c.add_c_register("a", 1) br = c.add_c_register("b", 1) c.H(qr[0]) c.Measure(qr[0], ar[0]) c.X(qr[1], condition=reg_eq(ar, 0)) c.Measure(qr[1], br[0]) backend = AerBackend() backend.compile_circuit(c) counts = backend.get_counts(c, 1024) for key in counts.keys(): assert key in {(0, 1), (1, 0)}
def pyquil_to_tk(prog: Program) -> Circuit: """ Convert a :py:class:`pyquil.Program` to a tket :py:class:`Circuit` . Note that not all pyQuil operations are currently supported by pytket. :param prog: A circuit to be converted :return: The converted circuit """ tkc = Circuit() qmap = {} for q in prog.get_qubits(): uid = Qubit("q", q) tkc.add_qubit(uid) qmap.update({q: uid}) cregmap: Dict = {} for i in prog.instructions: if isinstance(i, Gate): try: optype = _known_quil_gate[i.name] except KeyError as error: raise NotImplementedError("Operation not supported by tket: " + str(i)) from error qubits = [qmap[q.index] for q in i.qubits] params = [param_from_pyquil(p) for p in i.params] # type: ignore tkc.add_gate(optype, params, qubits) elif isinstance(i, Measurement): qubit = qmap[i.qubit.index] reg = cregmap[i.classical_reg.name] # type: ignore bit = reg[i.classical_reg.offset] # type: ignore tkc.Measure(qubit, bit) elif isinstance(i, Declare): if i.memory_type == "BIT": new_reg = tkc.add_c_register(i.name, i.memory_size) cregmap.update({i.name: new_reg}) elif i.memory_type == "REAL": continue else: raise NotImplementedError("Cannot handle memory of type " + i.memory_type) elif isinstance(i, Pragma): continue elif isinstance(i, Halt): return tkc else: raise NotImplementedError("PyQuil instruction is not a gate: " + str(i)) return tkc
def test_nondefault_registers() -> None: c = Circuit() qreg = c.add_q_register("g", 3) creg1 = c.add_c_register("a", 3) creg2 = c.add_c_register("b", 3) c.X(qreg[1]) c.X(qreg[0]) c.Measure(qreg[1], creg1[1]) c.Measure(qreg[0], creg2[0]) b = QsharpSimulatorBackend() b.compile_circuit(c) counts = b.get_result(b.process_circuit(c, 10)).get_counts() assert counts == {(0, 1, 0, 1, 0, 0): 10}
def test_condition_errors() -> None: with pytest.raises(Exception) as errorinfo: c = Circuit(2, 2) c.X(0, condition_bits=[0], condition_value=1) tk_to_qiskit(c) assert "OpenQASM conditions must be an entire register" in str( errorinfo.value) with pytest.raises(Exception) as errorinfo: c = Circuit(2, 2) b = c.add_c_register("b", 2) c.X(Qubit(0), condition_bits=[b[0], Bit(0)], condition_value=1) tk_to_qiskit(c) assert "OpenQASM conditions can only use a single register" in str( errorinfo.value) with pytest.raises(Exception) as errorinfo: c = Circuit(2, 2) c.X(0, condition_bits=[1, 0], condition_value=1) tk_to_qiskit(c) assert "OpenQASM conditions must be an entire register in order" in str( errorinfo.value)
Transform.DecomposeBoxes().apply(c) print(c.get_commands()) # Note that the unitaries have been decomposed into elementary gates. # ## Classical controls # Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs. # # For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\mathrm{Rz}(\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively. # # First, we'll add two classical wires to the circuit to store the measurement results: from pytket.circuit import Bit c.add_c_register("m", 2) m = [Bit("m", i) for i in range(2)] # Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure: q = [Qubit("q", i) for i in range(3)] c.X(q[0]) c.Measure(q[0], m[0]) c.Measure(q[1], m[1]) # Finally we add the classically conditioned Rz operation, using the `add_gate()` method: from pytket.circuit import OpType c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]],
class CircuitBuilder: def __init__( self, qregs: List[QuantumRegister], cregs: Optional[List[ClassicalRegister]] = None, name: Optional[str] = None, phase: Optional[float] = 0.0, ): self.qregs = qregs self.cregs = [] if cregs is None else cregs self.tkc = Circuit(name=name) self.tkc.add_phase(phase) self.qregmap = {} for reg in qregs: tk_reg = self.tkc.add_q_register(reg.name, len(reg)) self.qregmap.update({reg: tk_reg}) self.cregmap = {} for reg in self.cregs: tk_reg = self.tkc.add_c_register(reg.name, len(reg)) self.cregmap.update({reg: tk_reg}) def circuit(self) -> Circuit: return self.tkc def add_qiskit_data(self, data: "QuantumCircuitData") -> None: for i, qargs, cargs in data: condition_kwargs = {} if i.condition is not None: cond_reg = self.cregmap[i.condition[0]] condition_kwargs = { "condition_bits": [cond_reg[k] for k in range(len(cond_reg))], "condition_value": i.condition[1], } if type(i) == ControlledGate: if type(i.base_gate) == qiskit_gates.RYGate: optype = OpType.CnRy else: # Maybe handle multicontrolled gates in a more general way, # but for now just do CnRy raise NotImplementedError( "qiskit ControlledGate with " + "base gate {} not implemented".format(i.base_gate)) else: optype = _known_qiskit_gate[type(i)] qubits = [ self.qregmap[qbit.register][qbit.index] for qbit in qargs ] bits = [self.cregmap[bit.register][bit.index] for bit in cargs] if optype == OpType.Unitary2qBox: u = i.to_matrix() ubox = Unitary2qBox(u) self.tkc.add_unitary2qbox(ubox, qubits[0], qubits[1], **condition_kwargs) elif optype == OpType.Barrier: self.tkc.add_barrier(qubits) elif optype in (OpType.CircBox, OpType.Custom): qregs = [QuantumRegister(i.num_qubits, "q") ] if i.num_qubits > 0 else [] cregs = ([ClassicalRegister(i.num_clbits, "c")] if i.num_clbits > 0 else []) builder = CircuitBuilder(qregs, cregs) builder.add_qiskit_data(i.definition) subc = builder.circuit() if optype == OpType.CircBox: cbox = CircBox(subc) self.tkc.add_circbox(cbox, qubits + bits, **condition_kwargs) else: # warning, this will catch all `Gate` instances # that were not picked up as a subclass in _known_qiskit_gate params = [param_to_tk(p) for p in i.params] gate_def = CustomGateDef.define(i.name, subc, list(subc.free_symbols())) self.tkc.add_custom_gate(gate_def, params, qubits + bits) else: params = [param_to_tk(p) for p in i.params] self.tkc.add_gate(optype, params, qubits + bits, **condition_kwargs)