def test_layers_basic(self): """The layers() method returns a list of layers, each of them with a list of nodes.""" qreg = QuantumRegister(2, 'qr') creg = ClassicalRegister(2, 'cr') qubit0 = qreg[0] qubit1 = qreg[1] clbit0 = creg[0] clbit1 = creg[1] condition = (creg, 3) dag = DAGCircuit() dag.add_qreg(qreg) dag.add_creg(creg) dag.apply_operation_back(HGate(), [qubit0], []) dag.apply_operation_back(CnotGate(), [qubit0, qubit1], [], condition=None) dag.apply_operation_back(Measure(), [qubit1, clbit1], [], condition=None) dag.apply_operation_back(XGate(), [qubit1], [], condition=condition) dag.apply_operation_back(Measure(), [qubit0, clbit0], [], condition=None) dag.apply_operation_back(Measure(), [qubit1, clbit1], [], condition=None) layers = list(dag.layers()) self.assertEqual(5, len(layers)) name_layers = [[ node.op.name for node in layer["graph"].nodes() if node.type == "op" ] for layer in layers] self.assertEqual( [['h'], ['cx'], ['measure'], ['x'], ['measure', 'measure']], name_layers)
def test_layers_basic(self): """ The layers() method returns a list of layers, each of them with a list of nodes.""" qreg = QuantumRegister(2, 'qr') creg = ClassicalRegister(2, 'cr') qubit0 = qreg[0] qubit1 = qreg[1] clbit0 = creg[0] clbit1 = creg[1] condition = (creg, 3) dag = DAGCircuit() dag.add_basis_element('h', 1, 0, 0) dag.add_basis_element('cx', 2, 0, 0) dag.add_basis_element('x', 1, 0, 0) dag.add_basis_element('measure', 1, 1, 0) dag.add_qreg(qreg) dag.add_creg(creg) dag.apply_operation_back(HGate(qubit0)) dag.apply_operation_back(CnotGate(qubit0, qubit1), condition=None) dag.apply_operation_back(Measure(qubit1, clbit1), condition=None) dag.apply_operation_back(XGate(qubit1), condition=condition) dag.apply_operation_back(Measure(qubit0, clbit0), condition=None) dag.apply_operation_back(Measure(qubit1, clbit1), condition=None) layers = list(dag.layers()) self.assertEqual(5, len(layers)) name_layers = [[ node[1]["op"].name for node in layer["graph"].multi_graph.nodes(data=True) if node[1]["type"] == "op" ] for layer in layers] self.assertEqual( [['h'], ['cx'], ['measure'], ['x'], ['measure', 'measure']], name_layers)
def test_apply_operation_back_conditional_measure(self): """Test consistency of apply_operation_back for conditional measure.""" # Measure targeting a clbit which is not a member of the conditional # register. qc.measure(qr[0], cr[0]).c_if(cr2, 0) new_creg = ClassicalRegister(1, 'cr2') self.dag.add_creg(new_creg) meas_gate = Measure() meas_gate.condition = (new_creg, 0) meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit0], [self.clbit0]) self.assertEqual(meas_node.qargs, [self.qubit0]) self.assertEqual(meas_node.cargs, [self.clbit0]) self.assertEqual(meas_node.condition, meas_gate.condition) self.assertEqual( sorted(self.dag._multi_graph.in_edges(meas_node._node_id)), sorted([ (self.dag.input_map[self.qubit0]._node_id, meas_node._node_id, { 'wire': self.qubit0, 'name': 'qr[0]' }), (self.dag.input_map[self.clbit0]._node_id, meas_node._node_id, { 'wire': self.clbit0, 'name': 'cr[0]' }), (self.dag.input_map[new_creg[0]]._node_id, meas_node._node_id, { 'wire': Clbit(new_creg, 0), 'name': 'cr2[0]' }), ])) self.assertEqual( sorted(self.dag._multi_graph.out_edges(meas_node._node_id)), sorted([ (meas_node._node_id, self.dag.output_map[self.qubit0]._node_id, { 'wire': self.qubit0, 'name': 'qr[0]' }), (meas_node._node_id, self.dag.output_map[self.clbit0]._node_id, { 'wire': self.clbit0, 'name': 'cr[0]' }), (meas_node._node_id, self.dag.output_map[new_creg[0]]._node_id, { 'wire': Clbit(new_creg, 0), 'name': 'cr2[0]' }), ])) self.assertTrue(rx.is_directed_acyclic_graph(self.dag._multi_graph))
def test_append_resolves_numpy_integers(self, index): """Test that Numpy's integers can be used to reference qubits and clbits.""" qubits = [Qubit(), Qubit()] clbits = [Clbit(), Clbit()] test = QuantumCircuit(qubits, clbits) test.append(Measure(), [index], [index]) expected = QuantumCircuit(qubits, clbits) expected.append(Measure(), [qubits[int(index)]], [clbits[int(index)]]) self.assertEqual(test, expected)
def test_apply_operation_back_conditional_measure_to_self(self): """Test consistency of apply_operation_back for measure onto conditioning bit.""" # Measure targeting a clbit which _is_ a member of the conditional # register. qc.measure(qr[0], cr[0]).c_if(cr, 3) meas_gate = Measure() meas_gate.condition = self.condition meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit1], [self.clbit1]) self.assertEqual(meas_node.qargs, [self.qubit1]) self.assertEqual(meas_node.cargs, [self.clbit1]) self.assertEqual(meas_node.condition, meas_gate.condition) self.assertEqual( sorted(self.dag._multi_graph.in_edges(meas_node._node_id)), sorted([ (self.dag.input_map[self.qubit1]._node_id, meas_node._node_id, { 'wire': self.qubit1, 'name': 'qr[1]' }), (self.dag.input_map[self.clbit0]._node_id, meas_node._node_id, { 'wire': self.clbit0, 'name': 'cr[0]' }), (self.dag.input_map[self.clbit1]._node_id, meas_node._node_id, { 'wire': self.clbit1, 'name': 'cr[1]' }), ])) self.assertEqual( sorted(self.dag._multi_graph.out_edges(meas_node._node_id)), sorted([ (meas_node._node_id, self.dag.output_map[self.qubit1]._node_id, { 'wire': self.qubit1, 'name': 'qr[1]' }), (meas_node._node_id, self.dag.output_map[self.clbit0]._node_id, { 'wire': self.clbit0, 'name': 'cr[0]' }), (meas_node._node_id, self.dag.output_map[self.clbit1]._node_id, { 'wire': self.clbit1, 'name': 'cr[1]' }), ])) self.assertTrue(rx.is_directed_acyclic_graph(self.dag._multi_graph))
def test_apply_operation_back(self): """The apply_operation_back() method.""" self.dag.apply_operation_back(HGate(self.qubit0), condition=None) self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1), condition=None) self.dag.apply_operation_back(Measure(self.qubit1, self.clbit1), condition=None) self.dag.apply_operation_back(XGate(self.qubit1), condition=self.condition) self.dag.apply_operation_back(Measure(self.qubit0, self.clbit0), condition=None) self.dag.apply_operation_back(Measure(self.qubit1, self.clbit1), condition=None) self.assertEqual(len(self.dag.multi_graph.nodes), 16) self.assertEqual(len(self.dag.multi_graph.edges), 17)
def test_apply_operation_back(self): """The apply_operation_back() method.""" self.dag.apply_operation_back(HGate(), [self.qubit0], [], condition=None) self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], [], condition=None) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None) self.dag.apply_operation_back(XGate(), [self.qubit1], [], condition=self.condition) self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], [], condition=None) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None) self.assertEqual(len(list(self.dag.nodes())), 16) self.assertEqual(len(list(self.dag.edges())), 17)
def test_append_rejects_bits_not_in_circuit(self): """Test that append rejects bits that are not in the circuit.""" test = QuantumCircuit(2, 2) with self.subTest("qubit"), self.assertRaisesRegex(CircuitError, "not in the circuit"): test.append(Measure(), [Qubit()], [test.clbits[0]]) with self.subTest("clbit"), self.assertRaisesRegex(CircuitError, "not in the circuit"): test.append(Measure(), [test.qubits[0]], [Clbit()]) with self.subTest("qubit list"), self.assertRaisesRegex(CircuitError, "not in the circuit"): test.append(Measure(), [[test.qubits[0], Qubit()]], [test.clbits]) with self.subTest("clbit list"), self.assertRaisesRegex(CircuitError, "not in the circuit"): test.append(Measure(), [test.qubits], [[test.clbits[0], Clbit()]])
def test_append_resolves_slices(self, index): """Test that slices can be used to reference qubits and clbits with the same semantics that they have on lists.""" qregs = [QuantumRegister(2), QuantumRegister(1)] cregs = [ClassicalRegister(1), ClassicalRegister(2)] test = QuantumCircuit(*qregs, *cregs) test.append(Measure(), [index], [index]) expected = QuantumCircuit(*qregs, *cregs) for qubit, clbit in zip(expected.qubits[index], expected.clbits[index]): expected.append(Measure(), [qubit], [clbit]) self.assertEqual(test, expected)
def test_append_resolves_integers(self, index): """Test that integer arguments to append are correctly resolved.""" # We need to assume that appending ``Bit`` instances will always work, so we have something # to test against. qubits = [Qubit(), Qubit()] clbits = [Clbit(), Clbit()] test = QuantumCircuit(qubits, clbits) test.append(Measure(), [index], [index]) expected = QuantumCircuit(qubits, clbits) expected.append(Measure(), [qubits[index]], [clbits[index]]) self.assertEqual(test, expected)
def test_apply_operation_back(self): """The apply_operation_back() method.""" x_gate = XGate() x_gate.condition = self.condition self.dag.apply_operation_back(HGate(), [self.qubit0], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) self.dag.apply_operation_back(x_gate, [self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], []) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) self.assertEqual(len(list(self.dag.nodes())), 16) self.assertEqual(len(list(self.dag.edges())), 17)
def test_edges(self): """Test that DAGCircuit.edges() behaves as expected with ops.""" self.dag.apply_operation_back(HGate(), [self.qubit0], [], condition=None) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [], condition=None) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None) self.dag.apply_operation_back(XGate(), [self.qubit1], [], condition=self.condition) self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], [], condition=None) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None) out_edges = self.dag.edges(self.dag.output_map.values()) self.assertEqual(list(out_edges), []) in_edges = self.dag.edges(self.dag.input_map.values()) # number of edges for input nodes should be the same as number of wires self.assertEqual(len(list(in_edges)), 5)
def test_measure_one(self): """Test Measure.repeat(1) method. """ qr = QuantumRegister(1, 'qr') cr = ClassicalRegister(1, 'cr') expected_circ = QuantumCircuit(qr, cr) expected_circ.append(Measure(), [qr[0]], [cr[0]]) expected = expected_circ.to_instruction() result = Measure().repeat(1) self.assertEqual(result.name, 'measure*1') self.assertEqual(result.definition, expected.definition) self.assertIsInstance(result, Instruction) self.assertNotIsInstance(result, Gate)
def run(self, dag): """Run the OptimizeSwapBeforeMeasure pass on `dag`. Args: dag (DAGCircuit): the DAG to be optimized. Returns: DAGCircuit: the optimized DAG. """ swaps = dag.op_nodes(SwapGate) for swap in swaps: final_successor = [] for successor in dag.successors(swap): final_successor.append(successor.type == 'out' or (successor.type == 'op' and successor.op.name == 'measure')) if all(final_successor): # the node swap needs to be removed and, if a measure follows, needs to be adapted swap_qargs = swap.qargs measure_layer = DAGCircuit() for qreg in dag.qregs.values(): measure_layer.add_qreg(qreg) for creg in dag.cregs.values(): measure_layer.add_creg(creg) for successor in dag.successors(swap): if successor.type == 'op' and successor.op.name == 'measure': # replace measure node with a new one, where qargs is set with the "other" # swap qarg. dag.remove_op_node(successor) old_measure_qarg = successor.qargs[0] new_measure_qarg = swap_qargs[swap_qargs.index(old_measure_qarg) - 1] measure_layer.apply_operation_back(Measure(), [new_measure_qarg], [successor.cargs[0]]) dag.extend_back(measure_layer) dag.remove_op_node(swap) return dag
def test_can_append_to_quantum_circuit(self): """Test that we can add various objects with Operation interface to a Quantum Circuit.""" qc = QuantumCircuit(6, 1) qc.append(XGate(), [2]) qc.append(Barrier(3), [1, 2, 4]) qc.append(CXGate(), [0, 1]) qc.append(Measure(), [1], [0]) qc.append(Reset(), [0]) qc.cx(3, 4) qc.append(Gate("some_gate", 3, []), [1, 2, 3]) qc.append(Initialize([0.5, 0.5, 0.5, 0.5]), [4, 5]) qc.append(Isometry(np.eye(4, 4), 0, 0), [3, 4]) qc.append(Pauli("II"), [0, 1]) # Appending Clifford circ1 = QuantumCircuit(2) circ1.h(1) circ1.cx(0, 1) qc.append(Clifford(circ1), [0, 1]) # Appending CNOTDihedral circ2 = QuantumCircuit(2) circ2.t(0) circ2.x(0) circ2.t(1) qc.append(CNOTDihedral(circ2), [2, 3]) # If we got to here, we have successfully appended everything to qc self.assertIsInstance(qc, QuantumCircuit)
def test_quantum_successors(self): """The method dag.quantum_successors() returns successors connected by quantum edges""" # q_0: |0>─────■───|0>─ # ┌─┐┌─┴─┐ # q_1: |0>┤M├┤ X ├───── # └╥┘└───┘ # c_0: 0 ═╬═══════════ # ║ # c_1: 0 ═╩═══════════ self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) successor_measure = self.dag.quantum_successors( self.dag.named_nodes('measure').pop()) cnot_node = next(successor_measure) with self.assertRaises(StopIteration): next(successor_measure) self.assertIsInstance(cnot_node.op, CXGate) successor_cnot = self.dag.quantum_successors(cnot_node) # Ordering between Reset and out[q1] is indeterminant. successor1 = next(successor_cnot) successor2 = next(successor_cnot) with self.assertRaises(StopIteration): next(successor_cnot) self.assertTrue( (successor1.type == 'out' and isinstance(successor2.op, Reset)) or (successor2.type == 'out' and isinstance(successor1.op, Reset)))
def test_raise_if_substituting_dag_modifies_its_conditional(self): """Verify that we raise if the input dag modifies any of the bits in node.condition.""" # Our unroller's rely on substitute_node_with_dag carrying the condition # from the node over to the input dag, which it does by making every # node in the input dag conditioned over the same bits. However, if the # input dag e.g. measures to one of those bits, the behavior of the # remainder of the DAG would be different, so detect and raise in that # case. instr = Instruction('opaque', 1, 1, []) instr.control = self.condition instr_node = self.dag.apply_operation_back(instr, [self.qubit0], [self.clbit1], instr.control) sub_dag = DAGCircuit() sub_qr = QuantumRegister(1, 'sqr') sub_cr = ClassicalRegister(1, 'scr') sub_dag.add_qreg(sub_qr) sub_dag.add_creg(sub_cr) sub_dag.apply_operation_back(Measure(), [sub_qr[0]], [sub_cr[0]]) with self.assertRaises(DAGCircuitError): self.dag.substitute_node_with_dag(instr_node, sub_dag)
def test_append_rejects_bit_of_wrong_type(self): """Test that append rejects bits of the wrong type in an argument list.""" qubits = [Qubit(), Qubit()] clbits = [Clbit(), Clbit()] test = QuantumCircuit(qubits, clbits) with self.subTest("c to q"), self.assertRaisesRegex(CircuitError, "Incorrect bit type"): test.append(Measure(), [clbits[0]], [clbits[1]]) with self.subTest("q to c"), self.assertRaisesRegex(CircuitError, "Incorrect bit type"): test.append(Measure(), [qubits[0]], [qubits[1]]) with self.subTest("none to q"), self.assertRaisesRegex(CircuitError, "Incorrect bit type"): test.append(Measure(), [Bit()], [clbits[0]]) with self.subTest("none to c"), self.assertRaisesRegex(CircuitError, "Incorrect bit type"): test.append(Measure(), [qubits[0]], [Bit()]) with self.subTest("none list"), self.assertRaisesRegex(CircuitError, "Incorrect bit type"): test.append(Measure(), [[qubits[0], Bit()]], [[clbits[0], Bit()]])
def test_measure_as_operation(self): """Test that we can instantiate an object of class :class:`~qiskit.circuit.Measure` and that it has the expected name, num_qubits and num_clbits. """ op = Measure() self.assertTrue(op.name == "measure") self.assertTrue(op.num_qubits == 1) self.assertTrue(op.num_clbits == 1)
def test_substituting_node_with_wrong_width_node_raises(self): """Verify replacing a node with one of a different shape raises.""" dag = DAGCircuit() qr = QuantumRegister(2) dag.add_qreg(qr) node_to_be_replaced = dag.apply_operation_back(CXGate(), [qr[0], qr[1]]) with self.assertRaises(DAGCircuitError) as _: dag.substitute_node(node_to_be_replaced, Measure())
def test_raise_if_invalid_op_type_for_init(self): """Test exception is raised when input with invalid type are supplied.""" with self.assertRaises(NoiseError): QuantumError(Measure()) # instruction with clbits with self.assertRaises(NoiseError): QuantumError([Reset(), XGate()]) # list of instructions expecting default qubits with self.assertRaises(NoiseError): QuantumError([(Reset(), [0]), XGate()]) # partially supplied
def test_append_resolves_scalar_numpy_array(self): """Test that size-1 Numpy arrays can be used to index arguments. These arrays can be passed to ``int``, which means they sometimes might be involved in spurious casts.""" test = QuantumCircuit(1, 1) test.append(Measure(), [np.array([0])], [np.array([0])]) expected = QuantumCircuit(1, 1) expected.measure(0, 0) self.assertEqual(test, expected)
def test_quantum_successors(self): """The method dag.quantum_successors() returns successors connected by quantum edges""" self.dag.apply_operation_back(Measure(self.qubit1, self.clbit1)) self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1)) self.dag.apply_operation_back(Reset(self.qubit0)) successor_measure = self.dag.quantum_successors(self.dag.get_named_nodes('measure').pop()) self.assertEqual(len(successor_measure), 1) cnot_node = successor_measure[0] self.assertIsInstance(self.dag.multi_graph.nodes[cnot_node]["op"], CnotGate) successor_cnot = self.dag.quantum_successors(cnot_node) self.assertEqual(len(successor_cnot), 2) self.assertEqual(self.dag.multi_graph.nodes[successor_cnot[0]]["type"], 'out') self.assertIsInstance(self.dag.multi_graph.nodes[successor_cnot[1]]["op"], Reset)
def test_quantum_successors(self): """The method dag.quantum_successors() returns successors connected by quantum edges""" self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) successor_measure = self.dag.quantum_successors( self.dag.named_nodes('measure').pop()) self.assertEqual(len(successor_measure), 1) cnot_node = successor_measure[0] self.assertIsInstance(cnot_node.op, CnotGate) successor_cnot = self.dag.quantum_successors(cnot_node) self.assertEqual(len(successor_cnot), 2) self.assertEqual(successor_cnot[0].type, 'out') self.assertIsInstance(successor_cnot[1].op, Reset)
def test_quantum_predecessors(self): """The method dag.quantum_predecessors() returns predecessors connected by quantum edges""" self.dag.apply_operation_back(Reset(), [self.qubit0], []) self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) predecessor_measure = self.dag.quantum_predecessors( self.dag.named_nodes('measure').pop()) cnot_node = next(predecessor_measure) with self.assertRaises(StopIteration): next(predecessor_measure) self.assertIsInstance(cnot_node.op, CnotGate) predecessor_cnot = self.dag.quantum_predecessors(cnot_node) self.assertIsInstance(next(predecessor_cnot).op, Reset) self.assertEqual(next(predecessor_cnot).type, 'in') with self.assertRaises(StopIteration): next(predecessor_cnot)
def run(self, dag): """Run the OptimizeSwapBeforeMeasure pass on `dag`. Args: dag (DAGCircuit): the DAG to be optimized. Returns: DAGCircuit: the optimized DAG. """ swaps = dag.op_nodes(SwapGate) for swap in swaps[::-1]: if swap.op.condition is not None: continue final_successor = [] for successor in dag.successors(swap): final_successor.append( isinstance(successor, DAGOutNode) or (isinstance(successor, DAGOpNode) and isinstance(successor.op, Measure))) if all(final_successor): # the node swap needs to be removed and, if a measure follows, needs to be adapted swap_qargs = swap.qargs measure_layer = DAGCircuit() for qreg in dag.qregs.values(): measure_layer.add_qreg(qreg) for creg in dag.cregs.values(): measure_layer.add_creg(creg) for successor in list(dag.successors(swap)): if isinstance(successor, DAGOpNode) and isinstance( successor.op, Measure): # replace measure node with a new one, where qargs is set with the "other" # swap qarg. dag.remove_op_node(successor) old_measure_qarg = successor.qargs[0] new_measure_qarg = swap_qargs[ swap_qargs.index(old_measure_qarg) - 1] measure_layer.apply_operation_back( Measure(), [new_measure_qarg], [successor.cargs[0]]) dag.compose(measure_layer) dag.remove_op_node(swap) return dag
def test_measure_zero(self): """Test Measure.repeat(0) method. Raises, since n<1 """ with self.assertRaises(QiskitError) as context: _ = Measure().repeat(0) self.assertIn('strictly positive integer', str(context.exception))
def test_measure_minus_one(self): """Test Measure.repeat(-1) method. Raises, since n<1 """ with self.assertRaises(CircuitError) as context: _ = Measure().repeat(-1) self.assertIn('strictly positive integer', str(context.exception))