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_blank_wires() -> None: backends: List[_CirqBaseBackend] = [ CirqDensityMatrixSimBackend(), CirqStateSimBackend(), ] for b in backends: assert b.get_result(b.process_circuit(Circuit(2).X(0))).q_bits == {Qubit(0): 0} assert b.get_result(b.process_circuit(Circuit(2).X(1))).q_bits == {Qubit(1): 0} for r in b.get_result(b.process_circuit_moments(Circuit(2).X(0).X(0).X(0))): # type: ignore assert r.q_bits == {Qubit(0): 0} for r in b.get_result(b.process_circuit_moments(Circuit(2).X(1).X(1).X(1))): # type: ignore assert r.q_bits == {Qubit(1): 0} for b in [CirqDensityMatrixSampleBackend(), CirqStateSampleBackend()]: assert b.get_result( b.process_circuit(Circuit(2, 2).X(0).Measure(0, 0), 100) ).c_bits == {Bit(0): 0} assert b.get_result( b.process_circuit(Circuit(2, 2).X(1).Measure(1, 1), 100) ).c_bits == {Bit(1): 0} assert b.get_result( b.process_circuit(Circuit(2, 2).X(1).Measure(1, 0), 100) ).c_bits == {Bit(0): 0} assert b.get_result( b.process_circuit(Circuit(2, 2).X(1).Measure(0, 1), 100) ).c_bits == {Bit(1): 0}
def test_hadamard() -> None: c = MyCircuit([Qubit(0), Qubit(1)], [Bit(0), Bit(1)]) c.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2) c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) c.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2) c.add_measure(Qubit(0), Bit(0)) counts = get_counts(c, n_shots=100, seed=11) assert counts == {(0, 0): 50, (0, 1): 50}
def prepare_bits(bits, box, offset): renaming = dict() start = tk_circ.n_bits if not bits else 0\ if not offset else bits[offset - 1] + 1 for i in range(start, tk_circ.n_bits): old = Bit(i) new = Bit(i + len(box.cod)) renaming.update({old: new}) tk_circ.rename_units(renaming) for i in range(start, start + len(box.cod)): tk_circ.add_bit(Bit(i), offset=offset + i - start) return bits[:offset] + list(range(start, start + len(box.cod)))\ + [i + len(box.cod) for i in bits[offset:]]
def test_bell() -> None: c = MyCircuit([Qubit(0), Qubit(1)], [Bit(0), Bit(1)]) # Hadamard Q0 c.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2) c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) c.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2) # CX Q0 Q1 c.add_gate(QubitPauliString(Qubit(0), Pauli.Z), -np.pi / 2) c.add_gate(QubitPauliString(Qubit(1), Pauli.X), -np.pi / 2) c.add_gate(QubitPauliString([Qubit(0), Qubit(1)], [Pauli.Z, Pauli.X]), np.pi / 2) c.add_measure(Qubit(0), Bit(0)) c.add_measure(Qubit(1), Bit(1)) counts = get_counts(c, n_shots=100, seed=11) assert counts == {(0, 0): 50, (1, 1): 50}
def _add_single_qubit_op_to_circuit(cmd: ProjectQCommand, circ: Circuit) -> bool: assert len(cmd.qubits) == 1 assert len(cmd.qubits[0]) == 1 qubit_no = cmd.qubits[0][0].id new_qubit = False if get_control_count(cmd) > 0: raise Exception("singleq gate " + str(cmd.gate) + " has " + str(get_control_count(cmd)) + " control qubits") else: if qubit_no >= circ.n_qubits: circ.add_blank_wires(1 + qubit_no - circ.n_qubits) new_qubit = True if type(cmd.gate) == pqo.MeasureGate: bit = Bit("c", qubit_no) if bit not in circ.bits: circ.add_bit(bit) circ.Measure(qubit_no, qubit_no) return new_qubit elif type(cmd.gate) in (pqo.Rx, pqo.Ry, pqo.Rz): op = Op.create(_pq_to_tk_singleqs[type(cmd.gate)], cmd.gate.angle / np.pi) else: op = Op.create(_pq_to_tk_singleqs[type(cmd.gate)]) circ.add_gate(Op=op, args=[qubit_no]) return new_qubit
def test_conditional_rotation() -> None: c = MyCircuit([Qubit(0)], [Bit(0), Bit(1)]) # Randomise qubit state c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) c.add_measure(Qubit(0), Bit(0)) # Correct qubit state to |1> - this should only happen when the measurement was 0 c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi, {Bit(0): 0, Bit(1): 0}) # Randomise final measurement - this should never happen c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2, {Bit(0): 0, Bit(1): 1}) c.add_measure(Qubit(0), Bit(1)) counts = get_counts(c, n_shots=100, seed=11) assert counts == {(1, 0): 50, (1, 1): 50}
def test_multireg() -> None: b = HoneywellBackend(device_name="HQS-LT-1.0-APIVAL", label="test 3") c = Circuit() q1 = Qubit("q1", 0) q2 = Qubit("q2", 0) c1 = Bit("c1", 0) c2 = Bit("c2", 0) for q in (q1, q2): c.add_qubit(q) for cb in (c1, c2): c.add_bit(cb) c.H(q1) c.CX(q1, q2) c.Measure(q1, c1) c.Measure(q2, c2) b.compile_circuit(c) n_shots = 10 shots = b.get_shots(c, n_shots) assert np.array_equal(shots, np.zeros((10, 2)))
def test_conditional_measurement() -> None: c = MyCircuit([Qubit(0), Qubit(1)], [Bit(0), Bit(1), Bit(2), Bit(3)]) # Get a random number c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) c.add_measure(Qubit(0), Bit(0)) # If random number is 0 then flip to give |1> # Otherwise, randomly generate |+i> or |-i> c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) c.add_measure(Qubit(0), Bit(1), {Bit(0): 1}) c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) # Deterministic if Bit(0) == 0, random if Bit(0) == 1 c.add_measure(Qubit(0), Bit(2)) # Test end-of-circuit conditions by copying Bit(2) to Bit(3) c.add_gate(QubitPauliString(Qubit(1), Pauli.X), np.pi) c.add_measure(Qubit(1), Bit(3), {Bit(2): 1}) counts = get_counts(c, n_shots=10000, seed=11) assert counts[(1, 1, 0, 0)] == pytest.approx(5000, rel=0.02) assert counts[(0, 0, 0, 1)] == pytest.approx(1250, rel=0.02) assert counts[(1, 1, 0, 1)] == pytest.approx(1250, rel=0.02) assert counts[(0, 0, 1, 1)] == pytest.approx(1250, rel=0.02) assert counts[(1, 1, 1, 1)] == pytest.approx(1250, rel=0.02)
def test_convert_result() -> None: # testing fix to register order bug TKET-752 qr1 = QuantumRegister(1, name="q1") qr2 = QuantumRegister(2, name="q2") cr = ClassicalRegister(5, name="z") cr2 = ClassicalRegister(2, name="b") qc = QuantumCircuit(qr1, qr2, cr, cr2) qc.x(qr1[0]) qc.x(qr2[1]) # check statevector simulator = Aer.get_backend("statevector_simulator") qisk_result = execute(qc, simulator, shots=10).result() tk_res = next(qiskit_result_to_backendresult(qisk_result)) state = tk_res.get_state([Qubit("q2", 1), Qubit("q1", 0), Qubit("q2", 0)]) correct_state = np.zeros(1 << 3, dtype=complex) correct_state[6] = 1 + 0j assert compare_statevectors(state, correct_state) # check measured qc.measure(qr1[0], cr[0]) qc.measure(qr2[1], cr2[0]) simulator = Aer.get_backend("qasm_simulator") qisk_result = execute(qc, simulator, shots=10).result() tk_res = next(qiskit_result_to_backendresult(qisk_result)) one_bits = [Bit("z", 0), Bit("b", 0)] zero_bits = [Bit("z", i) for i in range(1, 5)] + [Bit("b", 1)] assert tk_res.get_counts(one_bits) == Counter({(1, 1): 10}) assert tk_res.get_counts(zero_bits) == Counter({(0, 0, 0, 0, 0): 10}) assert (qisk_result.results[0].data.to_dict() == backendresult_to_qiskit_resultdata(tk_res, qisk_result.results[0].header, None).to_dict())
def test_basic_ordering() -> None: # Test that final bit readouts are in the intended order (DLO) c = MyCircuit([Qubit(0)], [Bit(0), Bit(1), Bit(2)]) c.add_measure(Qubit(0), Bit(0)) c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi) c.add_measure(Qubit(0), Bit(2)) c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) c.add_measure(Qubit(0), Bit(1)) counts = get_counts(c, n_shots=100, seed=11) assert counts == {(1, 0, 0): 50, (1, 1, 0): 50}
def test_overwrite() -> None: # Test that classical data is overwritten by later measurements appropriately c = MyCircuit([Qubit(0)], [Bit(0), Bit(1)]) c.add_measure(Qubit(0), Bit(0)) c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi) c.add_measure(Qubit(0), Bit(0)) c.add_measure(Qubit(0), Bit(1)) c.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) c.add_measure(Qubit(0), Bit(1)) counts = get_counts(c, n_shots=100, seed=11) assert counts == {(0, 1): 50, (1, 1): 50}
def _convert_result(resultdict: Dict[str, List[str]]) -> BackendResult: array_dict = { creg: np.array([list(a) for a in reslist]).astype(np.uint8) for creg, reslist in resultdict.items() } reversed_creg_names = sorted(array_dict.keys(), reverse=True) c_bits = [ Bit(name, ind) for name in reversed_creg_names for ind in range(array_dict[name].shape[-1] - 1, -1, -1) ] stacked_array = np.hstack( [array_dict[name] for name in reversed_creg_names]) return BackendResult( c_bits=c_bits, shots=OutcomeArray.from_readouts( cast(Sequence[Sequence[int]], stacked_array)), )
def measure_qubits(qubits, bits, box, bit_offset, qubit_offset): if isinstance(box, Measure) and box.override_bits: for j, _ in enumerate(box.dom[:len(box.dom) // 2]): i_bit = bits[bit_offset + j] i_qubit = qubits[qubit_offset + j] tk_circ.Measure(i_qubit, i_bit) return bits, qubits for j, _ in enumerate(box.dom): i_bit, i_qubit = len(tk_circ.bits), qubits[qubit_offset + j] tk_circ.add_bit(Bit(i_bit)) tk_circ.Measure(i_qubit, i_bit) if isinstance(box, Bra): tk_circ.post_select({i_bit: box.bitstring[j]}) if isinstance(box, Measure): bits = bits[:bit_offset + j] + [i_bit] + bits[bit_offset + j:] if isinstance(box, Bra): qubits = qubits[:qubit_offset]\ + qubits[qubit_offset + len(box.dom):] return bits, qubits
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)
pass1.apply(cu) circ2 = cu.circuit print(tk_to_qiskit(circ2)) # Note that the pass we just ran also performed some clean-up: the SWAP gate was decomposed into three CX gates, one of which was cancelled by a preceding CX gate; the cancelling gates were removed from the circuit. # # Every compilation pass has associated sets of preconditions and postconditions on the circuit. If all preconditions are satisfied before the pass, all postconditions are guaranteed to be satisfied afterwards. When we apply a pass to a circuit, we can optionally pass `SafetyMode.Audit` as the second parameter; this will tell the pass to check all preconditions explicitly. By default, there is only limited checking of preconditions and `pytket` relies on the programmer assuring these. # # For example, the `NoClassicalControl` predicate is a precondition of the `PauliSimp` pass. Let's add a classically controlled gate to our circuit: from pytket.passes import PauliSimp, SafetyMode from pytket.circuit import Qubit, Bit q = [Qubit("q", i) for i in range(5)] c = Bit("c") circ.add_bit(c) circ.Measure(q[3], c) circ.CY(q[0], q[1], condition_bits=[c], condition_value=1) cu = CompilationUnit(circ) try: PauliSimp().apply(cu, safety_mode=SafetyMode.Audit) except RuntimeError as e: print("Error:", str(e)) # The preconditions and postconditions of all the elementary predicates are documented in their string representations: PauliSimp() # ## Backends and default passes
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]], condition_value=3)
def cirq_to_tk(circuit: cirq.circuits.Circuit) -> Circuit: """Converts a Cirq :py:class:`Circuit` to a tket :py:class:`Circuit` object. :param circuit: The input Cirq :py:class:`Circuit` :raises NotImplementedError: If the input contains a Cirq :py:class:`Circuit` operation which is not yet supported by pytket :return: The tket :py:class:`Circuit` corresponding to the input circuit """ tkcirc = Circuit() qmap = {} for qb in circuit.all_qubits(): if isinstance(qb, LineQubit): uid = Qubit("q", qb.x) elif isinstance(qb, GridQubit): uid = Qubit("g", qb.row, qb.col) elif isinstance(qb, cirq.ops.NamedQubit): uid = Qubit(qb.name) else: raise NotImplementedError("Cannot convert qubits of type " + str(type(qb))) tkcirc.add_qubit(uid) qmap.update({qb: uid}) for moment in circuit: for op in moment.operations: if isinstance(op, cirq.ops.GlobalPhaseOperation): tkcirc.add_phase(cmath.phase(op.coefficient) / pi) continue gate = op.gate gatetype = type(gate) qb_lst = [qmap[q] for q in op.qubits] if isinstance(gate, cirq_common.HPowGate) and gate.exponent == 1: gate = cirq_common.H elif (gatetype == cirq_common.CNotPowGate and cast(cirq_common.CNotPowGate, gate).exponent == 1): gate = cirq_common.CNOT elif (gatetype == cirq_pauli._PauliX and cast(cirq_pauli._PauliX, gate).exponent == 1): gate = cirq_pauli.X elif (gatetype == cirq_pauli._PauliY and cast(cirq_pauli._PauliY, gate).exponent == 1): gate = cirq_pauli.Y elif (gatetype == cirq_pauli._PauliZ and cast(cirq_pauli._PauliZ, gate).exponent == 1): gate = cirq_pauli.Z if gate in _constant_gates: try: optype = _cirq2ops_mapping[gate] except KeyError as error: raise NotImplementedError( "Operation not supported by tket: " + str(op.gate)) from error params = [] elif isinstance(gate, cirq_common.MeasurementGate): uid = Bit(gate.key) tkcirc.add_bit(uid) tkcirc.Measure(*qb_lst, uid) continue elif isinstance(gate, cirq.ops.PhasedXPowGate): optype = OpType.PhasedX pe = gate.phase_exponent params = [gate.exponent, pe] elif isinstance(gate, cirq.ops.FSimGate): optype = OpType.FSim params = [gate.theta / pi, gate.phi / pi] elif isinstance(gate, cirq.ops.PhasedISwapPowGate): optype = OpType.PhasedISWAP params = [gate.phase_exponent, gate.exponent] else: try: optype = _cirq2ops_mapping[gatetype] params = [cast(Any, gate).exponent] except (KeyError, AttributeError) as error: raise NotImplementedError( "Operation not supported by tket: " + str(op.gate)) from error tkcirc.add_gate(optype, params, qb_lst) return tkcirc
def test_empty() -> None: c = MyCircuit([Qubit(0), Qubit(1)], [Bit(0), Bit(1)]) counts = get_counts(c, n_shots=100, seed=11) assert counts == {(0, 0): 100}