diff --git a/crates/circuit/src/bit.rs b/crates/circuit/src/bit.rs index e5535741559..65c7252ede8 100644 --- a/crates/circuit/src/bit.rs +++ b/crates/circuit/src/bit.rs @@ -1,16 +1,17 @@ use std::hash::{DefaultHasher, Hash, Hasher}; -use pyo3::prelude::*; +use pyo3::{prelude::*, types::PyDict}; use crate::{ + circuit_data::CircuitError, interner::Interned, register::{Register, RegisterAsKey}, }; /// Object representing a Python bit, that allows us to keep backwards compatibility /// with the previous structure. -#[pyclass(name = "Bit")] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[pyclass(name = "Bit", module = "qiskit._accelerate.bit", subclass)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PyBit { register: Option, // Register identifier index: Option, // Index within Register @@ -20,10 +21,13 @@ pub struct PyBit { impl PyBit { #[new] #[pyo3(signature=(register=None, index=None))] - pub fn new(register: Option, index: Option) -> Self { - Self { - register, - index, + pub fn new(register: Option, index: Option) -> PyResult { + match (®ister, index) { + (None, None) => Ok(Self { register, index }), + (Some(_), Some(_)) => Ok(Self { register, index }), + _ => Err(CircuitError::new_err( + "You should provide both an index and a register, not just one of them.", + )), } } @@ -38,17 +42,103 @@ impl PyBit { slf.is(&other) } - fn __hash__(slf: Bound<'_, Self>) -> PyResult { - let borrowed = slf.borrow(); + fn __hash__(&self, py: Python<'_>) -> PyResult { + if let (Some(reg), Some(idx)) = (self.register.as_ref(), self.index) { + return (reg.reduce(), idx).to_object(py).bind(py).hash(); + } + + // If registers are unavailable, hash by pointer value. let mut hasher = DefaultHasher::new(); - borrowed.hash(&mut hasher); + let pointer_val = self as *const Self; + pointer_val.hash(&mut hasher); Ok(hasher.finish() as isize) } + + fn __copy__(slf: Bound) -> Bound { + slf + } + + #[pyo3(signature = (_memo=None))] + fn __deepcopy__<'py>( + slf: Bound<'py, Self>, + _memo: Option>, + ) -> PyResult> { + let borrowed: PyRef = slf.borrow(); + if borrowed.index.is_none() && borrowed.register.is_none() { + return Ok(slf); + } + let copy = slf + .get_type() + .call_method1("__new__", (slf.get_type(),))? + .downcast_into::()?; + let mut copy_mut = copy.borrow_mut(); + copy_mut.register = borrowed.register.clone(); + copy_mut.index = borrowed.index; + Ok(copy) + } + + fn __getstate__(slf: PyRef<'_, Self>) -> (Option<(String, u32)>, Option) { + ( + slf.register.as_ref().map(|reg| { + let (name, num_qubits) = reg.reduce(); + (name.to_string(), num_qubits) + }), + slf.index.as_ref().copied(), + ) + } + + fn __setstate__(mut slf: PyRefMut<'_, Self>, state: (Option<(String, u32)>, Option)) { + slf.register = state + .0 + .map(|(name, num_qubits)| RegisterAsKey::new(name.as_str(), num_qubits)); + slf.index = state.1; + } } +macro_rules! create_py_bit { + ($name:ident, $pyname:literal, $module:literal) => { + #[pyclass(name=$pyname, extends=PyBit, subclass, module=$module)] + #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct $name(); + + #[pymethods] + impl $name { + #[new] + #[pyo3(signature = (register=None, index=None))] + pub fn py_new( + register: Option, + index: Option, + ) -> PyResult<(Self, PyBit)> { + Ok((Self(), PyBit::new(register, index)?)) + } + } + }; +} + +// Create python instances +create_py_bit! {PyQubit, "Qubit", "qiskit._accelerate.bit"} +create_py_bit! {PyClbit, "Clbit", "qiskit._accelerate.bit"} + /// Keeps information about where a qubit is located within the circuit. #[derive(Debug, Clone)] pub struct BitInfo { register_idx: Interned, index: u32, } + +impl BitInfo { + pub fn new(register_idx: Interned, index: u32) -> Self { + Self { + register_idx, + index, + } + } + + pub fn register_index(&self) -> Interned { + self.register_idx + } + + pub fn index(&self) -> u32 { + self.index + } +} diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 0486cf75a15..619cd3ec885 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -125,6 +125,9 @@ impl From for BitType { } pub fn circuit(m: &Bound) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/crates/circuit/src/register.rs b/crates/circuit/src/register.rs index 3e3a826fdb0..6d383df036b 100644 --- a/crates/circuit/src/register.rs +++ b/crates/circuit/src/register.rs @@ -1,36 +1,40 @@ use indexmap::IndexSet; use pyo3::{intern, types::PyAnyMethods, FromPyObject}; -use std::hash::{DefaultHasher, Hash, Hasher}; +use std::{ + hash::{Hash, Hasher}, + sync::Mutex, +}; use crate::{ interner::{Interned, Interner}, Clbit, Qubit, }; +static REGISTER_INSTANCE_COUNTER: Mutex = Mutex::new(0); +static PREFIX: &str = "reg"; + /// This represents the hash value of a Register according to the register's /// name and number of qubits. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct RegisterAsKey(u64); +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RegisterAsKey(String, u32); impl RegisterAsKey { - pub fn new(name: Option<&str>, num_qubits: u32) -> Self { - let mut hasher = DefaultHasher::default(); - (name, num_qubits).hash(&mut hasher); - Self(hasher.finish()) + pub fn new(name: &str, num_qubits: u32) -> Self { + Self(name.to_string(), num_qubits) + } + + pub fn reduce(&self) -> (&str, u32) { + (self.0.as_str(), self.1) } } impl<'py> FromPyObject<'py> for RegisterAsKey { fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult { let (name, num_qubits) = ( - ob.getattr(intern!(ob.py(), "name"))? - .extract::>()?, - ob.getattr(intern!(ob.py(), "num_qubits"))?.extract()?, + ob.getattr(intern!(ob.py(), "name"))?.extract()?, + ob.len()? as u32, ); - Ok(RegisterAsKey::new( - name.as_deref(), - num_qubits, - )) + Ok(RegisterAsKey(name, num_qubits)) } } /// Described the desired behavior of a Register. @@ -53,11 +57,23 @@ macro_rules! create_register { #[derive(Debug, Clone, Eq)] pub struct $name { register: IndexSet<<$name as Register>::Bit>, - name: Option, + name: String, } impl $name { pub fn new(size: usize, name: Option) -> Self { + let name = if let Some(name) = name { + name + } else { + let count = if let Ok(ref mut count) = REGISTER_INSTANCE_COUNTER.try_lock() { + let curr = **count; + **count += 1; + curr + } else { + panic!("Could not access register counter.") + }; + format!("{}{}", PREFIX, count) + }; Self { register: (0..size).map(|bit| <$bit>::new(bit)).collect(), name, @@ -87,7 +103,7 @@ macro_rules! create_register { impl Hash for $name { fn hash(&self, state: &mut H) { - (self.name.as_ref(), self.len()).hash(state); + (self.name.as_str(), self.len()).hash(state); } }