Skip to content

Commit

Permalink
Merge again branch 'oxidize-dag' into oxidize-dag-layers-merge-base2
Browse files Browse the repository at this point in the history
This includes PR Qiskit#19 and other things.
  • Loading branch information
jlapeyre committed Jul 17, 2024
2 parents 3bf6ec4 + c1ba596 commit cdcb172
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 70 deletions.
170 changes: 101 additions & 69 deletions crates/circuit/src/dag_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

use crate::bit_data::BitData;
use crate::circuit_instruction::{
convert_py_to_operation_type, operation_type_and_data_to_py, CircuitInstruction,
convert_py_to_operation_type, operation_type_to_py, operation_type_and_data_to_py, CircuitInstruction,
ExtraInstructionAttributes, OperationTypeConstruct, PackedInstruction,
};
use crate::dag_node::{DAGInNode, DAGNode, DAGOpNode, DAGOutNode};
Expand Down Expand Up @@ -558,6 +558,8 @@ impl DAGCircuit {
));
}
self.clbits = BitData::new(py, "clbits".to_string());
self.clbit_input_map.clear();
self.clbit_output_map.clear();
self.add_clbits(py, clbits)
}

Expand Down Expand Up @@ -2102,7 +2104,9 @@ def _format(operand):
return Ok(false);
}
for (regname, self_bits) in self_qregs {
let self_bits = self_bits.getattr("_bits")?.downcast_into_exact::<PyList>()?;
let self_bits = self_bits
.getattr("_bits")?
.downcast_into_exact::<PyList>()?;
let other_bits = match other_qregs.get_item(regname)? {
Some(bits) => bits.getattr("_bits")?.downcast_into_exact::<PyList>()?,
None => return Ok(false),
Expand All @@ -2124,7 +2128,9 @@ def _format(operand):
}

for (regname, self_bits) in self_cregs {
let self_bits = self_bits.getattr("_bits")?.downcast_into_exact::<PyList>()?;
let self_bits = self_bits
.getattr("_bits")?
.downcast_into_exact::<PyList>()?;
let other_bits = match other_cregs.get_item(regname)? {
Some(bits) => bits.getattr("_bits")?.downcast_into_exact::<PyList>()?,
None => return Ok(false),
Expand Down Expand Up @@ -2625,7 +2631,7 @@ def _format(operand):
Ok(out_dict.unbind())
}

/// Replace an DAGOpNode with a single operation. qargs, cargs and
/// Replace a DAGOpNode with a single operation. qargs, cargs and
/// conditions for the new operation will be inferred from the node to be
/// replaced. The new operation will be checked to match the shape of the
/// replaced operation.
Expand Down Expand Up @@ -2655,71 +2661,92 @@ def _format(operand):
op: &Bound<PyAny>,
inplace: bool,
propagate_condition: bool,
) -> Py<PyAny> {
// if not isinstance(node, DAGOpNode):
// raise DAGCircuitError("Only DAGOpNodes can be replaced.")
//
// if node.op.num_qubits != op.num_qubits or node.op.num_clbits != op.num_clbits:
// raise DAGCircuitError(
// "Cannot replace node of width ({} qubits, {} clbits) with "
// "operation of mismatched width ({} qubits, {} clbits).".format(
// node.op.num_qubits, node.op.num_clbits, op.num_qubits, op.num_clbits
// )
// )
//
// # This might include wires that are inherent to the node, like in its `condition` or
// # `target` fields, so might be wider than `node.op.num_{qu,cl}bits`.
// current_wires = {wire for _, _, wire in self.edges(node)}
// new_wires = set(node.qargs) | set(node.cargs)
// if (new_condition := getattr(op, "condition", None)) is not None:
// new_wires.update(condition_resources(new_condition).clbits)
// elif isinstance(op, SwitchCaseOp):
// if isinstance(op.target, Clbit):
// new_wires.add(op.target)
// elif isinstance(op.target, ClassicalRegister):
// new_wires.update(op.target)
// else:
// new_wires.update(node_resources(op.target).clbits)
//
// if propagate_condition and not (
// isinstance(node.op, ControlFlowOp) or isinstance(op, ControlFlowOp)
// ):
// if new_condition is not None:
// raise DAGCircuitError(
// "Cannot propagate a condition to an operation that already has one."
// )
// if (old_condition := getattr(node.op, "condition", None)) is not None:
// if not isinstance(op, Instruction):
// raise DAGCircuitError("Cannot add a condition on a generic Operation.")
// if not isinstance(node.op, ControlFlowOp):
// op = op.c_if(*old_condition)
// else:
// op.condition = old_condition
// new_wires.update(condition_resources(old_condition).clbits)
//
// if new_wires != current_wires:
// # The new wires must be a non-strict subset of the current wires; if they add new wires,
// # we'd not know where to cut the existing wire to insert the new dependency.
// raise DAGCircuitError(
// f"New operation '{op}' does not span the same wires as the old node '{node}'."
// f" New wires: {new_wires}, old wires: {current_wires}."
// )
//
// if inplace:
// if op.name != node.op.name:
// self._increment_op(op)
// self._decrement_op(node.op)
// node.op = op
// return node
//
// new_node = copy.copy(node)
// new_node.op = op
// self._multi_graph[node._node_id] = new_node
// if op.name != node.op.name:
// self._increment_op(op)
// self._decrement_op(node.op)
// return new_node
todo!()
) -> PyResult<Py<PyAny>> {
let py = op.py();
// Extract information from node that is going to be replaced
let old_packed = match self.dag.node_weight(node.as_ref().node.unwrap()) {
Some(NodeType::Operation(old_packed)) => old_packed.clone(),
Some(_) => {
return Err(DAGCircuitError::new_err(
"'node' must be of type 'DAGOpNode'.",
))
}
None => return Err(DAGCircuitError::new_err("'node' not found in DAG.")),
};
// Extract information from new op
let new_op = convert_py_to_operation_type(py, op.clone().unbind())?;

// If either operation is a control-flow operation, propagate_condition is ignored
let new_condition = match propagate_condition
&& !new_op.operation.control_flow()
&& !node.instruction.operation.control_flow()
{
true => {
// if new_op has a condition, the condition can't be propagated from the old node
if new_op.condition.is_some() {
return Err(DAGCircuitError::new_err(
"Cannot propagate a condition to an operation that already has one.",
));
} else {
match &node.instruction.extra_attrs {
Some(extra_attrs) => &extra_attrs.condition,
None => &None,
}
}
}
// If propagate_condition is false or ignored, the new_op condition (if any) will be used
false => &new_op.condition,
};

// Clone op data, as it will be moved into the PackedInstruction
let new_op_data = new_op.clone();
let new_weight = NodeType::Operation(PackedInstruction::new(
new_op_data.operation,
old_packed.qubits_id,
old_packed.clbits_id,
new_op_data.params,
new_op_data.label,
new_op_data.duration,
new_op_data.unit,
new_condition.clone(),
#[cfg(feature = "cache_pygates")]
Some(op.clone().unbind()),
));

// Use self.dag.contract_nodes to replace a single node with an Operation
// Is there a better method to do it?
let new_node = self
.dag
.contract_nodes(
Some(node.as_ref().node.unwrap()).into_iter(),
new_weight,
false,
)
.unwrap();

// Update self.op_names
self.decrement_op(old_packed.op.name().to_string());
self.increment_op(new_op.operation.name().to_string());

// If inplace==True, pretend to apply op substitution in-place, else return new node
if !inplace {
Ok(self.get_node(py, new_node)?)
} else {
Ok(operation_type_to_py(
py,
&node.instruction.replace(
py,
Some(new_op.operation.into()),
Some(node.instruction.qubits.clone().into_any().bind(py)),
Some(node.instruction.clbits.clone().into_any().bind(py)),
Some(new_op.params),
new_op.label,
new_op.duration,
new_op.unit,
new_condition.clone(),
)?,
)?)
}
}

/// Decompose the circuit into sets of qubits with no gates connecting them.
Expand Down Expand Up @@ -3814,6 +3841,11 @@ def _format(operand):
.iter()?
.unbind())
}

fn _has_edge(&self, source: usize, target: usize) -> bool {
self.dag
.contains_edge(NodeIndex::new(source), NodeIndex::new(target))
}
}

impl DAGCircuit {
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/passes/utils/merge_adjacent_barriers.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _collect_potential_merges(dag, barriers):
for next_barrier in barriers[1:]:

# Ensure barriers are adjacent before checking if they are mergeable.
if dag._multi_graph.has_edge(end_of_barrier._node_id, next_barrier._node_id):
if dag._has_edge(end_of_barrier._node_id, next_barrier._node_id):

# Remove all barriers that have already been included in this new barrier from the
# set of ancestors/descendants as they will be removed from the new DAG when it is
Expand Down

0 comments on commit cdcb172

Please sign in to comment.