Skip to content

Commit

Permalink
Establish CGates with non-default control states as non-standard in c…
Browse files Browse the repository at this point in the history
…ircuit_instruction.rs. Add unit test.
  • Loading branch information
ElePT committed Jun 28, 2024
1 parent 325cf6c commit 9b47121
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 9 deletions.
29 changes: 23 additions & 6 deletions crates/circuit/src/circuit_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use pyo3::{intern, IntoPy, PyObject, PyResult};
use smallvec::{smallvec, SmallVec};

use crate::imports::{
get_std_gate_class, populate_std_gate_map, GATE, INSTRUCTION, OPERATION,
get_std_gate_class, populate_std_gate_map, CONTROLLED_GATE, GATE, INSTRUCTION, OPERATION,
SINGLETON_CONTROLLED_GATE, SINGLETON_GATE, WARNINGS_WARN,
};
use crate::interner::Index;
Expand Down Expand Up @@ -831,20 +831,37 @@ pub(crate) fn convert_py_to_operation_type(
},
Err(_) => None,
};
// If the input instruction is a standard gate and a singleton instance
// If the input instruction is a standard gate and a singleton instance,
// we should check for mutable state. A mutable instance should be treated
// as a custom gate not a standard gate because it has custom properties.
//
// In the futuer we can revisit this when we've dropped `duration`, `unit`,
// Controlled gates with non-default control states are also considered
// custom gates even if a standard representation exists for the default
// control state.

// In the future we can revisit this when we've dropped `duration`, `unit`,
// and `condition` from the api as we should own the label in the
// `CircuitInstruction`. The other piece here is for controlled gates there
// is the control state, so for `SingletonControlledGates` we'll still need
// this check.
if standard.is_some() {
let mutable: bool = py_op.getattr(py, intern!(py, "mutable"))?.extract(py)?;
if mutable
// The default ctrl_states are 1, "1" and None. These are the only cases where controlled
// gates can be standard.
let is_default_ctrl_state: bool = match py_op.getattr(py, intern!(py, "ctrl_state")) {
Ok(c_state) => match c_state.extract::<Option<i32>>(py) {
Ok(c_state_int) => match c_state_int {
Some(c_int) => c_int == 1,
None => true,
},
Err(_) => false,
},
Err(_) => false,
};

if (mutable
&& (py_op_bound.is_instance(SINGLETON_GATE.get_bound(py))?
|| py_op_bound.is_instance(SINGLETON_CONTROLLED_GATE.get_bound(py))?)
|| py_op_bound.is_instance(SINGLETON_CONTROLLED_GATE.get_bound(py))?))
|| (py_op_bound.is_instance(CONTROLLED_GATE.get_bound(py))? && !is_default_ctrl_state)
{
standard = None;
}
Expand Down
2 changes: 2 additions & 0 deletions crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ 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 CONTROLLED_GATE: ImportOnceCell =
ImportOnceCell::new("qiskit.circuit", "ControlledGate");

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

Expand Down
6 changes: 5 additions & 1 deletion crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1308,7 +1308,11 @@ impl Operation for StandardGate {
3,
[
(Self::HGate, smallvec![], smallvec![Qubit(2)]),
(Self::CCXGate, smallvec![], smallvec![Qubit(0), Qubit(1), Qubit(2)]),
(
Self::CCXGate,
smallvec![],
smallvec![Qubit(0), Qubit(1), Qubit(2)],
),
(Self::HGate, smallvec![], smallvec![Qubit(2)]),
],
FLOAT_ZERO,
Expand Down
39 changes: 38 additions & 1 deletion test/python/circuit/test_rust_equivalence.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
import numpy as np

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library.standard_gates import *
from qiskit.circuit.library.standard_gates import C3XGate, U1Gate, ZGate
from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping
from qiskit.quantum_info import Operator

SKIP_LIST = {"rx", "ry", "ecr"}
CUSTOM_MAPPING = {"x", "rz"}
Expand Down Expand Up @@ -160,3 +161,39 @@ def test_num_params(self):
self.assertEqual(
len(gate_class.params), standard_gate.num_params, msg=f"{name} not equal"
)

def test_non_default_controls(self):
"""Test that controlled gates with a non-default ctrl_state
are not using the standard rust representation."""

z_gate = ZGate()
u1_gate = U1Gate(0.1)

z_term = -1
u1_term = 0.99500417 + 0.09983342j

for gate, term in zip([z_gate, u1_gate], [z_term, u1_term]):
default_ctrl_gates = [
gate.control(1, ctrl_state=1),
gate.control(1, ctrl_state="1"),
gate.control(1, ctrl_state=None),
]
non_default_ctrl_gates = [
gate.control(1, ctrl_state=0),
gate.control(1, ctrl_state="0"),
]
default_ctrl_op, non_default_ctrl_op = np.diag(np.ones(4, dtype=complex)), np.diag(
np.ones(4, dtype=complex)
)
default_ctrl_op[3, 3] = term
non_default_ctrl_op[2, 2] = term

for gate in default_ctrl_gates:
circuit = QuantumCircuit(2)
circuit.append(gate, [0, 1])
np.testing.assert_almost_equal(Operator(circuit).to_matrix(), default_ctrl_op)

for gate in non_default_ctrl_gates:
circuit = QuantumCircuit(2)
circuit.append(gate, [0, 1])
np.testing.assert_almost_equal(Operator(circuit).to_matrix(), non_default_ctrl_op)
2 changes: 1 addition & 1 deletion test/python/visualization/test_circuit_text_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5208,7 +5208,7 @@ def test_open_controlled_u1(self):
expected = "\n".join(
[
" ",
"qr_0: |0>──────────o─────────o─────────o─────────■────────",
"qr_0: |0>─o─────────o─────────o─────────o─────────■────────",
" │U1(0.1) │ │ │ │ ",
"qr_1: |0>─■─────────o─────────■─────────■─────────o────────",
" │U1(0.2) │U1(0.3) │ │ ",
Expand Down

0 comments on commit 9b47121

Please sign in to comment.