Skip to content

Commit

Permalink
Add path to avoid StandardGate conversion in circuit_to_dag
Browse files Browse the repository at this point in the history
  • Loading branch information
mtreinish committed Jun 29, 2024
1 parent d70cebe commit 48ec3ce
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 14 deletions.
11 changes: 4 additions & 7 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::circuit_instruction::{
convert_py_to_operation_type, CircuitInstruction, ExtraInstructionAttributes, OperationInput,
PackedInstruction,
};
use crate::imports::{BUILTIN_LIST, QUBIT};
use crate::imports::{BUILTIN_LIST, DEEPCOPY, QUBIT};
use crate::interner::{IndexedInterner, Interner, InternerKey};
use crate::operations::{Operation, OperationType, Param, StandardGate};
use crate::parameter_table::{ParamEntry, ParamTable, GLOBAL_PHASE_INDEX};
Expand Down Expand Up @@ -487,20 +487,17 @@ impl CircuitData {
res.param_table.clone_from(&self.param_table);

if deepcopy {
let deepcopy = py
.import_bound(intern!(py, "copy"))?
.getattr(intern!(py, "deepcopy"))?;
for inst in &mut res.data {
match &mut inst.op {
OperationType::Standard(_) => {}
OperationType::Gate(ref mut op) => {
op.gate = deepcopy.call1((&op.gate,))?.unbind();
op.gate = DEEPCOPY.get_bound(py).call1((&op.gate,))?.unbind();
}
OperationType::Instruction(ref mut op) => {
op.instruction = deepcopy.call1((&op.instruction,))?.unbind();
op.instruction = DEEPCOPY.get_bound(py).call1((&op.instruction,))?.unbind();
}
OperationType::Operation(ref mut op) => {
op.operation = deepcopy.call1((&op.operation,))?.unbind();
op.operation = DEEPCOPY.get_bound(py).call1((&op.operation,))?.unbind();
}
};
#[cfg(feature = "cache_pygates")]
Expand Down
1 change: 1 addition & 0 deletions crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub static SINGLETON_GATE: ImportOnceCell =
ImportOnceCell::new("qiskit.circuit.singleton", "SingletonGate");
pub static SINGLETON_CONTROLLED_GATE: ImportOnceCell =
ImportOnceCell::new("qiskit.circuit.singleton", "SingletonControlledGate");
pub static DEEPCOPY: ImportOnceCell = ImportOnceCell::new("copy", "deepcopy");

pub static WARNINGS_WARN: ImportOnceCell = ImportOnceCell::new("warnings", "warn");

Expand Down
32 changes: 31 additions & 1 deletion crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use std::f64::consts::PI;

use crate::circuit_data::CircuitData;
use crate::imports::{PARAMETER_EXPRESSION, QUANTUM_CIRCUIT};
use crate::imports::{DEEPCOPY, PARAMETER_EXPRESSION, QUANTUM_CIRCUIT};
use crate::{gate_matrix, Qubit};

use ndarray::{aview2, Array2};
Expand Down Expand Up @@ -1185,6 +1185,16 @@ impl PyInstruction {
instruction,
}
}

fn __deepcopy__(&self, py: Python, _memo: PyObject) -> PyResult<Self> {
Ok(PyInstruction {
qubits: self.qubits,
clbits: self.clbits,
params: self.params,
op_name: self.op_name.clone(),
instruction: DEEPCOPY.get_bound(py).call1((&self.instruction,))?.unbind(),
})
}
}

impl Operation for PyInstruction {
Expand Down Expand Up @@ -1264,6 +1274,16 @@ impl PyGate {
gate,
}
}

fn __deepcopy__(&self, py: Python, _memo: PyObject) -> PyResult<Self> {
Ok(PyGate {
qubits: self.qubits,
clbits: self.clbits,
params: self.params,
op_name: self.op_name.clone(),
gate: DEEPCOPY.get_bound(py).call1((&self.gate,))?.unbind(),
})
}
}

impl Operation for PyGate {
Expand Down Expand Up @@ -1356,6 +1376,16 @@ impl PyOperation {
operation,
}
}

fn __deepcopy__(&self, py: Python, _memo: PyObject) -> PyResult<Self> {
Ok(PyOperation {
qubits: self.qubits,
clbits: self.clbits,
params: self.params,
op_name: self.op_name.clone(),
operation: DEEPCOPY.get_bound(py).call1((&self.operation,))?.unbind(),
})
}
}

impl Operation for PyOperation {
Expand Down
25 changes: 20 additions & 5 deletions qiskit/converters/circuit_to_dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"""Helper function for converting a circuit to a dag"""
import copy

from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.dagcircuit.dagcircuit import DAGCircuit, DAGOpNode
from qiskit._accelerate.circuit import StandardGate


def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_order=None):
Expand Down Expand Up @@ -93,10 +94,24 @@ def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_ord
dagcircuit.add_creg(register)

for instruction in circuit.data:
op = instruction.operation
if copy_operations:
op = copy.deepcopy(op)
dagcircuit.apply_operation_back(op, instruction.qubits, instruction.clbits, check=False)
if not isinstance(instruction._raw_op, StandardGate):
op = instruction.operation
if copy_operations:
op = copy.deepcopy(op)
dagcircuit.apply_operation_back(op, instruction.qubits, instruction.clbits, check=False)
else:
node = DAGOpNode(
instruction._raw_op,
qargs=instruction.qubits,
cargs=instruction.clbits,
params=instruction.params,
label=instruction.label,
duration=instruction.duration,
unit=instruction.unit,
condition=instruction.condition,
dag=dagcircuit,
)
dagcircuit._apply_op_node_back(node)

dagcircuit.duration = circuit.duration
dagcircuit.unit = circuit.unit
Expand Down
11 changes: 10 additions & 1 deletion qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,11 @@ def copy_empty_like(self, *, vars_mode: _VarsMode = "alike"):
return target_dag

def _apply_op_node_back(self, node: DAGOpNode):
additional = ()
if _may_have_additional_wires(node):
# This is the slow path; most of the time, this won't happen.
additional = set(_additional_wires(node)).difference(node.cargs)

node._node_id = self._multi_graph.add_node(node)
self._increment_op(node)

Expand All @@ -726,7 +731,11 @@ def _apply_op_node_back(self, node: DAGOpNode):
# and adding new edges from the operation node to each output node
self._multi_graph.insert_node_on_in_edges_multiple(
node._node_id,
[self.output_map[bit]._node_id for bits in (node.qargs, node.cargs) for bit in bits],
[
self.output_map[bit]._node_id
for bits in (node.qargs, node.cargs, additional)
for bit in bits
],
)

def apply_operation_back(
Expand Down

0 comments on commit 48ec3ce

Please sign in to comment.