def test_extend_is_validated(self): """Verify extending circuit.data is broadcast and validated.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.data.extend( [ CircuitInstruction(HGate(), [qr[0]], []), CircuitInstruction(CXGate(), [0, 1], []), CircuitInstruction(HGate(), [qr[1]], []), ] ) expected_qc = QuantumCircuit(qr) expected_qc.h(0) expected_qc.cx(0, 1) expected_qc.h(1) self.assertEqual(qc, expected_qc) self.assertRaises( CircuitError, qc.data.extend, [CircuitInstruction(HGate(), [qr[0], qr[1]], [])] ) self.assertRaises(CircuitError, qc.data.extend, [CircuitInstruction(HGate(), [], [qr[0]])])
def test_contains(self): """Verify checking if a inst/qarg/carg tuple is in circuit.data.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) self.assertTrue(CircuitInstruction(HGate(), [qr[0]], []) in qc.data) self.assertFalse(CircuitInstruction(HGate(), [qr[1]], []) in qc.data) self.assertFalse(CircuitInstruction(XGate(), [qr[0]], []) in qc.data)
def test_add_delay_on_single_qubit_to_circuit(self): qc = QuantumCircuit(1) qc.h(0) qc.delay(100, 0) qc.delay(200, [0]) qc.delay(300, qc.qubits[0]) self.assertEqual( qc.data[1], CircuitInstruction(Delay(duration=100), qc.qubits, [])) self.assertEqual( qc.data[2], CircuitInstruction(Delay(duration=200), qc.qubits, [])) self.assertEqual( qc.data[3], CircuitInstruction(Delay(duration=300), qc.qubits, []))
def test_index_gates(self): """Verify finding the index of a inst/qarg/carg tuple in circuit.data.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) qc.cx(0, 1) qc.h(1) qc.h(0) self.assertEqual(qc.data.index(CircuitInstruction(HGate(), [qr[0]], [])), 0) self.assertEqual(qc.data.index(CircuitInstruction(CXGate(), [qr[0], qr[1]], [])), 1) self.assertEqual(qc.data.index(CircuitInstruction(HGate(), [qr[1]], [])), 2)
def test_getitem_by_insertion_order(self): """Verify one can get circuit.data items in insertion order.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) qc.cx(0, 1) qc.h(1) data = qc.data self.assertEqual(data[0], CircuitInstruction(HGate(), [qr[0]], [])) self.assertEqual(data[1], CircuitInstruction(CXGate(), [qr[0], qr[1]], [])) self.assertEqual(data[2], CircuitInstruction(HGate(), [qr[1]], []))
def test_iter(self): """Verify circuit.data can behave as an iterator.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) qc.cx(0, 1) qc.h(1) iter_ = iter(qc.data) self.assertEqual(next(iter_), CircuitInstruction(HGate(), [qr[0]], [])) self.assertEqual(next(iter_), CircuitInstruction(CXGate(), [qr[0], qr[1]], [])) self.assertEqual(next(iter_), CircuitInstruction(HGate(), [qr[1]], [])) self.assertRaises(StopIteration, next, iter_)
def dagdependency_to_circuit(dagdependency): """Build a ``QuantumCircuit`` object from a ``DAGDependency``. Args: dagdependency (DAGDependency): the input dag. Return: QuantumCircuit: the circuit representing the input dag dependency. """ name = dagdependency.name or None circuit = QuantumCircuit( dagdependency.qubits, dagdependency.clbits, *dagdependency.qregs.values(), *dagdependency.cregs.values(), name=name, ) circuit.metadata = dagdependency.metadata circuit.calibrations = dagdependency.calibrations for node in dagdependency.get_nodes(): circuit._append( CircuitInstruction(node.op.copy(), node.qargs, node.cargs)) return circuit
def _write_custom_operation(file_obj, name, operation, custom_operations): type_key = type_keys.CircuitInstruction.assign(operation) has_definition = False size = 0 data = None num_qubits = operation.num_qubits num_clbits = operation.num_clbits ctrl_state = 0 num_ctrl_qubits = 0 base_gate = None new_custom_instruction = [] if type_key == type_keys.CircuitInstruction.PAULI_EVOL_GATE: has_definition = True data = common.data_to_binary(operation, _write_pauli_evolution_gate) size = len(data) elif type_key == type_keys.CircuitInstruction.CONTROLLED_GATE: # For ControlledGate, we have to access and store the private `_definition` rather than the # public one, because the public one is mutated to include additional logic if the control # state is open, and the definition setter (during a subsequent read) uses the "fully # excited" control definition only. has_definition = True data = common.data_to_binary(operation._definition, write_circuit) size = len(data) num_ctrl_qubits = operation.num_ctrl_qubits ctrl_state = operation.ctrl_state base_gate = operation.base_gate elif operation.definition is not None: has_definition = True data = common.data_to_binary(operation.definition, write_circuit) size = len(data) if base_gate is None: base_gate_raw = b"" else: with io.BytesIO() as base_gate_buffer: new_custom_instruction = _write_instruction( base_gate_buffer, CircuitInstruction(base_gate, (), ()), custom_operations, {}) base_gate_raw = base_gate_buffer.getvalue() name_raw = name.encode(common.ENCODE) custom_operation_raw = struct.pack( formats.CUSTOM_CIRCUIT_INST_DEF_V2_PACK, len(name_raw), type_key, num_qubits, num_clbits, has_definition, size, num_ctrl_qubits, ctrl_state, len(base_gate_raw), ) file_obj.write(custom_operation_raw) file_obj.write(name_raw) if data: file_obj.write(data) file_obj.write(base_gate_raw) return new_custom_instruction
def test_slice(self): """Verify circuit.data can be sliced.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) qc.cx(0, 1) qc.h(1) qc.cx(1, 0) qc.h(1) qc.cx(0, 1) qc.h(0) h_slice = qc.data[::2] cx_slice = qc.data[1:-1:2] self.assertEqual( h_slice, [ CircuitInstruction(HGate(), [qr[0]], []), CircuitInstruction(HGate(), [qr[1]], []), CircuitInstruction(HGate(), [qr[1]], []), CircuitInstruction(HGate(), [qr[0]], []), ], ) self.assertEqual( cx_slice, [ CircuitInstruction(CXGate(), [qr[0], qr[1]], []), CircuitInstruction(CXGate(), [qr[1], qr[0]], []), CircuitInstruction(CXGate(), [qr[0], qr[1]], []), ], )
def test_insert_is_validated(self): """Verify inserting gates via circuit.data are broadcast and validated.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.data.insert(0, CircuitInstruction(HGate(), [qr[0]], [])) qc.data.insert(1, CircuitInstruction(CXGate(), [0, 1], [])) qc.data.insert(2, CircuitInstruction(HGate(), [qr[1]], [])) expected_qc = QuantumCircuit(qr) expected_qc.h(0) expected_qc.cx(0, 1) expected_qc.h(1) self.assertEqual(qc, expected_qc) self.assertRaises( CircuitError, qc.data.insert, 0, CircuitInstruction(HGate(), [qr[0], qr[1]], []) ) self.assertRaises(CircuitError, qc.data.insert, 0, CircuitInstruction(HGate(), [], [qr[0]]))
def test_count_gates(self): """Verify circuit.data can count inst/qarg/carg tuples.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) qc.x(0) qc.h(1) qc.h(0) data = qc.data self.assertEqual(data.count(CircuitInstruction(HGate(), [qr[0]], [])), 2)
def dag_to_circuit(dag): """Build a ``QuantumCircuit`` object from a ``DAGCircuit``. Args: dag (DAGCircuit): the input dag. Return: QuantumCircuit: the circuit representing the input dag. Example: .. jupyter-execute:: from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.dagcircuit import DAGCircuit from qiskit.converters import circuit_to_dag from qiskit.circuit.library.standard_gates import CHGate, U2Gate, CXGate from qiskit.converters import dag_to_circuit %matplotlib inline q = QuantumRegister(3, 'q') c = ClassicalRegister(3, 'c') circ = QuantumCircuit(q, c) circ.h(q[0]) circ.cx(q[0], q[1]) circ.measure(q[0], c[0]) circ.rz(0.5, q[1]).c_if(c, 2) dag = circuit_to_dag(circ) circuit = dag_to_circuit(dag) circuit.draw() """ name = dag.name or None circuit = QuantumCircuit( dag.qubits, dag.clbits, *dag.qregs.values(), *dag.cregs.values(), name=name, global_phase=dag.global_phase, ) circuit.metadata = dag.metadata circuit.calibrations = dag.calibrations for node in dag.topological_op_nodes(): circuit._append( CircuitInstruction(node.op.copy(), node.qargs, node.cargs)) circuit.duration = dag.duration circuit.unit = dag.unit return circuit
def test_setting_data_is_validated(self): """Verify setting circuit.data is broadcast and validated.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.data = [ CircuitInstruction(HGate(), [qr[0]], []), CircuitInstruction(CXGate(), [0, 1], []), CircuitInstruction(HGate(), [qr[1]], []), ] expected_qc = QuantumCircuit(qr) expected_qc.h(0) expected_qc.cx(0, 1) expected_qc.h(1) self.assertEqual(qc, expected_qc) with self.assertRaises(CircuitError): qc.data = [CircuitInstruction(HGate(), [qr[0], qr[1]], [])] with self.assertRaises(CircuitError): qc.data = [CircuitInstruction(HGate(), [], [qr[0]])]
def test_pop_gate(self): """Verify removing a gate via circuit.data.pop.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) qc.cx(0, 1) qc.h(1) last_h = qc.data.pop() expected_qc = QuantumCircuit(qr) expected_qc.h(0) expected_qc.cx(0, 1) self.assertEqual(qc, expected_qc) self.assertEqual(last_h, CircuitInstruction(HGate(), [qr[1]], []))
def test_remove_gate(self): """Verify removing a gate via circuit.data.remove.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.h(0) qc.cx(0, 1) qc.h(1) qc.h(0) qc.data.remove(CircuitInstruction(HGate(), [qr[0]], [])) expected_qc = QuantumCircuit(qr) expected_qc.cx(0, 1) expected_qc.h(1) expected_qc.h(0) self.assertEqual(qc, expected_qc)
def repeat(self, n): """Creates an instruction with `gate` repeated `n` amount of times. Args: n (int): Number of times to repeat the instruction Returns: qiskit.circuit.Instruction: Containing the definition. Raises: CircuitError: If n < 1. """ if int(n) != n or n < 1: raise CircuitError( "Repeat can only be called with strictly positive integer.") n = int(n) instruction = self._return_repeat(n) qargs = [] if self.num_qubits == 0 else QuantumRegister( self.num_qubits, "q") cargs = [] if self.num_clbits == 0 else ClassicalRegister( self.num_clbits, "c") if instruction.definition is None: # pylint: disable=cyclic-import from qiskit.circuit import QuantumCircuit, CircuitInstruction qc = QuantumCircuit() if qargs: qc.add_register(qargs) if cargs: qc.add_register(cargs) circuit_instruction = CircuitInstruction(self, qargs, cargs) for _ in [None] * n: qc._append(circuit_instruction) instruction.definition = qc return instruction
def inverse(self) -> "QFT": """Invert this circuit. Returns: The inverted circuit. """ if self.name in ("QFT", "IQFT"): name = "QFT" if self._inverse else "IQFT" else: name = self.name + "_dg" inverted = self.copy(name=name) # data consists of the QFT gate only iqft = self.data[0].operation.inverse() iqft.name = name inverted.data.clear() inverted._append(CircuitInstruction(iqft, inverted.qubits, [])) inverted._inverse = not self._inverse return inverted
def _read_instruction(file_obj, circuit, registers, custom_operations, version, vectors): if version < 5: instruction = formats.CIRCUIT_INSTRUCTION._make( struct.unpack( formats.CIRCUIT_INSTRUCTION_PACK, file_obj.read(formats.CIRCUIT_INSTRUCTION_SIZE), )) else: instruction = formats.CIRCUIT_INSTRUCTION_V2._make( struct.unpack( formats.CIRCUIT_INSTRUCTION_V2_PACK, file_obj.read(formats.CIRCUIT_INSTRUCTION_V2_SIZE), )) gate_name = file_obj.read(instruction.name_size).decode(common.ENCODE) label = file_obj.read(instruction.label_size).decode(common.ENCODE) condition_register = file_obj.read( instruction.condition_register_size).decode(common.ENCODE) qargs = [] cargs = [] params = [] condition_tuple = None if instruction.has_condition: # If an invalid register name is used assume it's a single bit # condition and treat the register name as a string of the clbit index if ClassicalRegister.name_format.match(condition_register) is None: # If invalid register prefixed with null character it's a clbit # index for single bit condition if condition_register[0] == "\x00": conditional_bit = int(condition_register[1:]) condition_tuple = (circuit.clbits[conditional_bit], instruction.condition_value) else: raise ValueError( f"Invalid register name: {condition_register} for condition register of " f"instruction: {gate_name}") else: condition_tuple = (registers["c"][condition_register], instruction.condition_value) if circuit is not None: qubit_indices = dict(enumerate(circuit.qubits)) clbit_indices = dict(enumerate(circuit.clbits)) else: qubit_indices = {} clbit_indices = {} # Load Arguments if circuit is not None: for _qarg in range(instruction.num_qargs): qarg = formats.CIRCUIT_INSTRUCTION_ARG._make( struct.unpack( formats.CIRCUIT_INSTRUCTION_ARG_PACK, file_obj.read(formats.CIRCUIT_INSTRUCTION_ARG_SIZE), )) if qarg.type.decode(common.ENCODE) == "c": raise TypeError("Invalid input carg prior to all qargs") qargs.append(qubit_indices[qarg.size]) for _carg in range(instruction.num_cargs): carg = formats.CIRCUIT_INSTRUCTION_ARG._make( struct.unpack( formats.CIRCUIT_INSTRUCTION_ARG_PACK, file_obj.read(formats.CIRCUIT_INSTRUCTION_ARG_SIZE), )) if carg.type.decode(common.ENCODE) == "q": raise TypeError("Invalid input qarg after all qargs") cargs.append(clbit_indices[carg.size]) # Load Parameters for _param in range(instruction.num_parameters): type_key, data_bytes = common.read_generic_typed_data(file_obj) param = _loads_instruction_parameter(type_key, data_bytes, version, vectors) params.append(param) # Load Gate object if gate_name in {"Gate", "Instruction", "ControlledGate"}: inst_obj = _parse_custom_operation(custom_operations, gate_name, params, version, vectors, registers) inst_obj.condition = condition_tuple if instruction.label_size > 0: inst_obj.label = label if circuit is None: return inst_obj circuit._append(inst_obj, qargs, cargs) return None elif gate_name in custom_operations: inst_obj = _parse_custom_operation(custom_operations, gate_name, params, version, vectors, registers) inst_obj.condition = condition_tuple if instruction.label_size > 0: inst_obj.label = label if circuit is None: return inst_obj circuit._append(inst_obj, qargs, cargs) return None elif hasattr(library, gate_name): gate_class = getattr(library, gate_name) elif hasattr(circuit_mod, gate_name): gate_class = getattr(circuit_mod, gate_name) elif hasattr(extensions, gate_name): gate_class = getattr(extensions, gate_name) elif hasattr(quantum_initializer, gate_name): gate_class = getattr(quantum_initializer, gate_name) elif hasattr(controlflow, gate_name): gate_class = getattr(controlflow, gate_name) else: raise AttributeError("Invalid instruction type: %s" % gate_name) if gate_name in {"IfElseOp", "WhileLoopOp"}: gate = gate_class(condition_tuple, *params) elif version >= 5 and issubclass(gate_class, ControlledGate): if gate_name in {"MCPhaseGate", "MCU1Gate"}: gate = gate_class(*params, instruction.num_ctrl_qubits) else: gate = gate_class(*params) gate.num_ctrl_qubits = instruction.num_ctrl_qubits gate.ctrl_state = instruction.ctrl_state gate.condition = condition_tuple else: if gate_name in {"Initialize", "UCRXGate", "UCRYGate", "UCRZGate"}: gate = gate_class(params) else: if gate_name == "Barrier": params = [len(qargs)] elif gate_name in {"BreakLoopOp", "ContinueLoopOp"}: params = [len(qargs), len(cargs)] gate = gate_class(*params) gate.condition = condition_tuple if instruction.label_size > 0: gate.label = label if circuit is None: return gate if not isinstance(gate, Instruction): circuit.append(gate, qargs, cargs) else: circuit._append(CircuitInstruction(gate, qargs, cargs)) return None
def apply_grad_gate( circuit, gate, param_index, grad_gate, grad_coeff, qr_superpos, open_ctrl=False, trim_after_grad_gate=False, ): """Util function to apply a gradient gate for the linear combination of unitaries method. Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit. Args: circuit (QuantumCircuit): The circuit in which to do the replacements. gate (Gate): The gate instance to replace. param_index (int): The index of the parameter in ``gate``. grad_gate (Gate): A controlled gate encoding the gradient of ``gate``. grad_coeff (float): A coefficient to the gradient component. Might not be one if the gradient contains multiple summed terms. qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit`` that is used as control for ``grad_gate``. open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed. trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can be used to reduce the circuit depth in e.g. computing an overlap of gradients. Returns: QuantumCircuit: A copy of the original circuit with the gradient gate added. Raises: RuntimeError: If ``gate`` is not in ``circuit``. """ qr_superpos_qubits = tuple(qr_superpos) # copy the input circuit taking the gates by reference out = QuantumCircuit(*circuit.qregs) out._data = circuit._data.copy() out._parameter_table = ParameterTable({ param: values.copy() for param, values in circuit._parameter_table.items() }) # get the data index and qubits of the target gate TODO use built-in gate_idx, gate_qubits = None, None for i, instruction in enumerate(out._data): if instruction.operation is gate: gate_idx, gate_qubits = i, instruction.qubits break if gate_idx is None: raise RuntimeError( "The specified gate could not be found in the circuit data.") # initialize replacement instructions replacement = [] # insert the phase fix before the target gate better documentation sign = np.sign(grad_coeff) is_complex = np.iscomplex(grad_coeff) if sign < 0 and is_complex: replacement.append( CircuitInstruction(SdgGate(), qr_superpos_qubits, ())) elif sign < 0: replacement.append( CircuitInstruction(ZGate(), qr_superpos_qubits, ())) elif is_complex: replacement.append( CircuitInstruction(SGate(), qr_superpos_qubits, ())) # else no additional gate required # open control if specified if open_ctrl: replacement += [ CircuitInstruction(XGate(), qr_superpos_qubits, []) ] # compute the replacement if isinstance(gate, UGate) and param_index == 0: theta = gate.params[2] rz_plus, rz_minus = RZGate(theta), RZGate(-theta) replacement += [ CircuitInstruction(rz_plus, (qubit, ), ()) for qubit in gate_qubits ] replacement += [ CircuitInstruction(RXGate(np.pi / 2), (qubit, ), ()) for qubit in gate_qubits ] replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, [])) replacement += [ CircuitInstruction(RXGate(-np.pi / 2), (qubit, ), ()) for qubit in gate_qubits ] replacement += [ CircuitInstruction(rz_minus, (qubit, ), ()) for qubit in gate_qubits ] # update parametertable if necessary if isinstance(theta, ParameterExpression): # This dangerously subverts ParameterTable by abusing the fact that binding will # mutate the exact instruction instance, and relies on all instances of `rz_plus` # that were added before being the same in memory, which QuantumCircuit usually # ensures is not the case. I'm leaving this as close to its previous form as # possible, to avoid introducing further complications, but this whole method # accesses internal attributes of `QuantumCircuit` and needs rewriting. # - Jake Lishman, 2022-03-02. out._update_parameter_table( CircuitInstruction(rz_plus, (gate_qubits[0], ), ())) out._update_parameter_table( CircuitInstruction(rz_minus, (gate_qubits[0], ), ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) if not trim_after_grad_gate: replacement.append(CircuitInstruction(gate, gate_qubits, ())) elif isinstance(gate, UGate) and param_index == 1: # gradient gate is applied after the original gate in this case replacement.append(CircuitInstruction(gate, gate_qubits, ())) replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) else: replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) if not trim_after_grad_gate: replacement.append(CircuitInstruction(gate, gate_qubits, ())) # replace the parameter we compute the derivative of with the replacement # TODO can this be done more efficiently? if trim_after_grad_gate: # remove everything after the gradient gate out._data[gate_idx:] = replacement # reset parameter table table = ParameterTable() for instruction in out._data: for idx, param_expression in enumerate( instruction.operation.params): if isinstance(param_expression, ParameterExpression): for param in param_expression.parameters: if param not in table.keys(): table[param] = ParameterReferences( ((instruction.operation, idx), )) else: table[param].add((instruction.operation, idx)) out._parameter_table = table else: out._data[gate_idx:gate_idx + 1] = replacement return out