Skip to content

Commit

Permalink
Expose only StandardInstructionType to Python.
Browse files Browse the repository at this point in the history
This way, we can also use this enum to tag the Python classes.
  • Loading branch information
kevinhartman committed Nov 22, 2024
1 parent 2c6888d commit 69a80cc
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 65 deletions.
26 changes: 15 additions & 11 deletions crates/circuit/src/circuit_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::imports::{
};
use crate::operations::{
Operation, OperationRef, Param, PyGate, PyInstruction, PyOperation, StandardGate,
StandardInstruction,
StandardInstruction, StandardInstructionType,
};
use crate::packed_instruction::PackedOperation;

Expand Down Expand Up @@ -702,21 +702,25 @@ impl<'py> FromPyObject<'py> for OperationFromPython {
});
}
'standard_instr: {
// We check the *instance* for a `_standard_instruction` field since standard
// instructions (unlike standard gates) can have a payload and are thus stateful.
let Some(standard) = ob
.getattr(intern!(py, "_standard_instruction"))
.and_then(|standard| standard.extract::<StandardInstruction>())
let Some(standard_type) = ob_type
.getattr(intern!(py, "_standard_instruction_type"))
.and_then(|standard| standard.extract::<StandardInstructionType>())
.ok()
else {
break 'standard_instr;
};
match standard {
StandardInstruction::Barrier(num_bits) => {
println!("Extracted barrier with size {:?}", num_bits);
let standard = match standard_type {
StandardInstructionType::Barrier => {
let num_qubits = ob.getattr(intern!(py, "num_qubits"))?.extract()?;
StandardInstruction::Barrier(num_qubits)
}
_ => ()
}
StandardInstructionType::Delay => {
let unit = ob.getattr(intern!(py, "__unit"))?.extract()?;
StandardInstruction::Delay(unit)
}
StandardInstructionType::Measure => StandardInstruction::Measure(),
StandardInstructionType::Reset => StandardInstruction::Reset(),
};
return Ok(OperationFromPython {
operation: PackedOperation::from_standard_instruction(standard),
params: extract_params()?,
Expand Down
3 changes: 1 addition & 2 deletions crates/circuit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ pub fn circuit(m: &Bound<PyModule>) -> PyResult<()> {
m.add_class::<dag_node::DAGOutNode>()?;
m.add_class::<dag_node::DAGOpNode>()?;
m.add_class::<operations::StandardGate>()?;
m.add_class::<operations::StandardInstruction>()?;
m.add_class::<operations::DelayUnit>()?;
m.add_class::<operations::StandardInstructionType>()?;
Ok(())
}

Expand Down
55 changes: 45 additions & 10 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use smallvec::{smallvec, SmallVec};

use numpy::IntoPyArray;
use numpy::PyReadonlyArray2;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{IntoPyDict, PyFloat, PyIterator, PyList, PyTuple};
use pyo3::{intern, IntoPy, Python};
Expand Down Expand Up @@ -289,7 +290,6 @@ impl<'a> Operation for OperationRef<'a> {

#[derive(Clone, Debug, Copy, Eq, PartialEq, Hash)]
#[repr(u8)]
#[pyclass(module = "qiskit._accelerate.circuit", eq, eq_int)]
pub enum DelayUnit {
NS,
PS,
Expand Down Expand Up @@ -325,8 +325,51 @@ impl fmt::Display for DelayUnit {
}
}

impl<'py> FromPyObject<'py> for DelayUnit {
fn extract_bound(b: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
let str: String = b.extract()?;
Ok(match str.as_str() {
"ns" => DelayUnit::NS,
"ps" => DelayUnit::PS,
"us" => DelayUnit::US,
"ms" => DelayUnit::MS,
"s" => DelayUnit::S,
"dt" => DelayUnit::DT,
unknown_unit => {
return Err(PyValueError::new_err(format!(
"Unit '{}' is invalid.",
unknown_unit
)));
}
})
}
}

/// An internal type used to further discriminate the payload of a `PackedOperation` when its
/// discriminant is `PackedOperationType::StandardInstruction`.
///
/// This is also used to tag standard instructions via the `_standard_instruction` class attribute
/// in the corresponding Python class.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[pyclass(module = "qiskit._accelerate.circuit", eq, eq_int)]
#[repr(u8)]
pub(crate) enum StandardInstructionType {
Barrier = 0,
Delay = 1,
Measure = 2,
Reset = 3,
}

unsafe impl ::bytemuck::CheckedBitPattern for StandardInstructionType {
type Bits = u8;

fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits < 4
}
}
unsafe impl ::bytemuck::NoUninit for StandardInstructionType {}

#[derive(Clone, Debug, Copy, Eq, PartialEq, Hash)]
#[pyclass(module = "qiskit._accelerate.circuit", eq)]
pub enum StandardInstruction {
Barrier(usize),
Delay(DelayUnit),
Expand Down Expand Up @@ -403,14 +446,6 @@ impl Operation for StandardInstruction {
}
}

#[pymethods]
impl StandardInstruction {
fn __getstate__(&self, py: Python) -> PyObject {
let siasdfe = *self as usize;
siasdfe.into_py(py)
}
}

impl StandardInstruction {
pub fn create_py_op(
&self,
Expand Down
24 changes: 2 additions & 22 deletions crates/circuit/src/packed_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::imports::{get_std_gate_class, BARRIER, DEEPCOPY, DELAY, MEASURE, RESE
use crate::interner::Interned;
use crate::operations::{
DelayUnit, Operation, OperationRef, Param, PyGate, PyInstruction, PyOperation, StandardGate,
StandardInstruction,
StandardInstruction, StandardInstructionType,
};
use crate::{Clbit, Qubit};

Expand Down Expand Up @@ -57,26 +57,6 @@ unsafe impl ::bytemuck::CheckedBitPattern for PackedOperationType {
}
unsafe impl ::bytemuck::NoUninit for PackedOperationType {}

/// A private type used to further discriminate the payload of a `PackedOperation` when its
/// discriminant is `PackedOperationType::StandardInstruction`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
enum StandardInstructionType {
Barrier = 0,
Delay = 1,
Measure = 2,
Reset = 3,
}

unsafe impl ::bytemuck::CheckedBitPattern for StandardInstructionType {
type Bits = u8;

fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits < 4
}
}
unsafe impl ::bytemuck::NoUninit for StandardInstructionType {}

/// A bit-packed `OperationType` enumeration.
///
/// This is logically equivalent to:
Expand Down Expand Up @@ -171,7 +151,7 @@ impl PackedOperation {
/// A bitmask that masks out a standard instruction's payload data. After masking, the resulting
/// integer must be shifted downwards again by the payload shift amount.
const STANDARD_INSTRUCTION_PAYLOAD_MASK: usize =
(u32::MAX as usize) << Self::STANDARD_INSTRUCTION_BITS + Self::DISCRIMINANT_BITS;
(u32::MAX as usize) << (Self::STANDARD_INSTRUCTION_BITS + Self::DISCRIMINANT_BITS);
/// A bitmask that retrieves the stored pointer directly. The discriminant is stored in the
/// low pointer bits that are guaranteed to be 0 by alignment, so no shifting is required.
const POINTER_MASK: usize = usize::MAX ^ Self::DISCRIMINANT_MASK;
Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/barrier.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from qiskit.exceptions import QiskitError
from qiskit.utils import deprecate_func
from .instruction import Instruction
from qiskit._accelerate.circuit import StandardInstruction
from qiskit._accelerate.circuit import StandardInstructionType


class Barrier(Instruction):
Expand All @@ -32,6 +32,7 @@ class Barrier(Instruction):
"""

_directive = True
_standard_instruction_type = StandardInstructionType.Barrier

def __init__(self, num_qubits: int, label: str | None = None):
"""
Expand All @@ -41,7 +42,6 @@ def __init__(self, num_qubits: int, label: str | None = None):
"""
self._label = label
super().__init__("barrier", num_qubits, 0, [], label=label)
self._standard_instruction = StandardInstruction.Barrier(num_qubits)

def inverse(self, annotated: bool = False):
"""Special case. Return self."""
Expand Down
17 changes: 4 additions & 13 deletions qiskit/circuit/delay.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,28 @@
from qiskit.circuit import _utils
from qiskit.circuit.parameterexpression import ParameterExpression
from qiskit.utils import deprecate_func
from qiskit._accelerate.circuit import StandardInstruction, DelayUnit
from qiskit._accelerate.circuit import StandardInstructionType


@_utils.with_gate_array(np.eye(2, dtype=complex))
class Delay(Instruction):
"""Do nothing and just delay/wait/idle for a specified duration."""

_standard_instruction_type = StandardInstructionType.Delay

def __init__(self, duration, unit="dt"):
"""
Args:
duration: the length of time of the duration. Given in units of ``unit``.
unit: the unit of the duration. Must be ``"dt"`` or an SI-prefixed seconds unit.
"""
units = {
"s": DelayUnit.S,
"ms": DelayUnit.MS,
"us": DelayUnit.US,
"ns": DelayUnit.NS,
"ps": DelayUnit.PS,
"dt": DelayUnit.DT,
}
if delay_unit := units.get(unit, None) is None:
if unit not in {"s", "ms", "us", "ns", "ps", "dt"}:
raise CircuitError(f"Unknown unit {unit} is specified.")
# Double underscore to differentiate from the private attribute in
# `Instruction`. This can be changed to `_unit` in 2.0 after we
# remove `unit` and `duration` from the standard instruction model
# as it only will exist in `Delay` after that point.
self.__unit = unit
# TODO: how can the delay be updated in the setter?
# TODO: looks like Delay is a Parameter type, so this is disabled for now
# self._standard_instruction = StandardInstruction.Delay(duration, delay_unit)
super().__init__("delay", 1, 0, params=[duration])

broadcast_arguments = Gate.broadcast_arguments
Expand Down
6 changes: 3 additions & 3 deletions qiskit/circuit/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@

from qiskit.circuit.singleton import SingletonInstruction, stdlib_singleton_key
from qiskit.circuit.exceptions import CircuitError
from qiskit._accelerate.circuit import StandardInstruction
from qiskit._accelerate.circuit import StandardInstructionType


class Measure(SingletonInstruction):
"""Quantum measurement in the computational basis."""

_standard_instruction_type = StandardInstructionType.Measure

def __init__(self, label=None, *, duration=None, unit="dt"):
"""
Args:
label: optional string label for this instruction.
"""
super().__init__("measure", 1, 1, [], label=label, duration=duration, unit=unit)
# TODO: store stateless standard instructions like measure on the class
self._standard_instruction = StandardInstruction.Measure

_singleton_lookup_key = stdlib_singleton_key()

Expand Down
5 changes: 3 additions & 2 deletions qiskit/circuit/reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@
"""

from qiskit.circuit.singleton import SingletonInstruction, stdlib_singleton_key
from qiskit._accelerate.circuit import StandardInstruction
from qiskit._accelerate.circuit import StandardInstructionType


class Reset(SingletonInstruction):
r"""Incoherently reset a qubit to the :math:`\lvert0\rangle` state."""

_standard_instruction_type = StandardInstructionType.Reset

def __init__(self, label=None, *, duration=None, unit="dt"):
"""
Args:
label: optional string label of this instruction.
"""
super().__init__("reset", 1, 0, [], label=label, duration=duration, unit=unit)
self._standard_instruction = StandardInstruction.Reset

_singleton_lookup_key = stdlib_singleton_key()

Expand Down

0 comments on commit 69a80cc

Please sign in to comment.