use std::ops::Range;
use bit_set::BitSet;
use std::hash::{Hash, Hasher};
use crate::RealScalar;
#[derive(Clone, Debug)]
pub enum ParamIndices {
Joint(usize, usize),
Disjoint(Vec<usize>),
}
impl PartialEq for ParamIndices {
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
match (self, other) {
(ParamIndices::Joint(start1, len1), ParamIndices::Joint(start2, len2)) => {
start1 == start2 && len1 == len2
}
(ParamIndices::Disjoint(v1), ParamIndices::Disjoint(v2)) => v1 == v2,
_ => self.iter().eq(other.iter()),
}
}
}
impl Eq for ParamIndices {}
impl Hash for ParamIndices {
fn hash<H: Hasher>(&self, state: &mut H) {
self.len().hash(state);
for index in self.iter() {
index.hash(state);
}
}
}
impl ParamIndices {
pub fn to_bitset(&self) -> BitSet {
let mut bitset = BitSet::new();
match self {
ParamIndices::Joint(start, length) => {
for i in 0..*length {
bitset.insert(*start + i);
}
}
ParamIndices::Disjoint(indices) => {
for index in indices {
bitset.insert(*index);
}
}
}
bitset
}
pub fn empty() -> ParamIndices {
ParamIndices::Joint(0, 0)
}
pub fn is_empty(&self) -> bool {
match self {
ParamIndices::Joint(_, length) => *length == 0,
ParamIndices::Disjoint(v) => v.is_empty(),
}
}
pub fn is_consecutive(&self) -> bool {
match self {
ParamIndices::Joint(_, _) => true,
ParamIndices::Disjoint(v) => {
if v.len() <= 1 {
return true;
}
let mut clone_v = v.clone();
clone_v.sort();
for i in 1..clone_v.len() {
if clone_v[i] != clone_v[i - 1] + 1 {
return false;
}
}
true
}
}
}
pub fn num_params(&self) -> usize {
match self {
ParamIndices::Joint(_, length) => *length,
ParamIndices::Disjoint(v) => v.len(),
}
}
pub fn start(&self) -> usize {
match self {
ParamIndices::Joint(start, _) => *start,
ParamIndices::Disjoint(v) => *v.first().unwrap_or(&0),
}
}
pub fn union(&self, other: &ParamIndices) -> ParamIndices {
match (self, other) {
(ParamIndices::Joint(start1, length1), ParamIndices::Joint(start2, length2)) => {
if *start2 > *start1 && *start2 < *start1 + *length1 {
ParamIndices::Joint(*start1, *start2 + *length2 - *start1)
} else if *start1 > *start2 && *start1 < *start2 + *length2 {
ParamIndices::Joint(*start2, *start1 + *length1 - *start2)
} else {
let mut indices = Vec::new();
for i in *start1..*start1 + *length1 {
indices.push(i);
}
for i in *start2..*start2 + *length2 {
indices.push(i);
}
ParamIndices::Disjoint(indices)
}
}
(ParamIndices::Joint(start, length), ParamIndices::Disjoint(v)) => {
let mut indices = Vec::new();
for i in *start..*start + *length {
if v.contains(&i) {
continue;
}
indices.push(i);
}
indices.extend(v.iter());
ParamIndices::Disjoint(indices)
}
(ParamIndices::Disjoint(v), ParamIndices::Joint(start, length)) => {
let mut indices = Vec::new();
for i in *start..*start + *length {
if v.contains(&i) {
continue;
}
indices.push(i);
}
indices.extend(v.iter());
ParamIndices::Disjoint(indices)
}
(ParamIndices::Disjoint(v1), ParamIndices::Disjoint(v2)) => {
let mut indices = Vec::new();
for i in v1 {
if v2.contains(i) {
continue;
}
indices.push(*i);
}
indices.extend(v2.iter());
ParamIndices::Disjoint(indices)
}
}
}
pub fn intersect(&self, other: &ParamIndices) -> ParamIndices {
match (self, other) {
(ParamIndices::Joint(start1, length1), ParamIndices::Joint(start2, length2)) => {
let mut indices = Vec::new();
for i in *start1..*start1 + *length1 {
if i >= *start2 && i < *start2 + *length2 {
indices.push(i);
}
}
ParamIndices::Disjoint(indices)
}
(ParamIndices::Joint(start, length), ParamIndices::Disjoint(v)) => {
let mut indices = Vec::new();
for i in *start..*start + *length {
if v.contains(&i) {
indices.push(i);
}
}
ParamIndices::Disjoint(indices)
}
(ParamIndices::Disjoint(v), ParamIndices::Joint(start, length)) => {
let mut indices = Vec::new();
for i in *start..*start + *length {
if v.contains(&i) {
indices.push(i);
}
}
ParamIndices::Disjoint(indices)
}
(ParamIndices::Disjoint(v1), ParamIndices::Disjoint(v2)) => {
let mut indices = Vec::new();
for i in v1 {
if v2.contains(i) {
indices.push(*i);
}
}
ParamIndices::Disjoint(indices)
}
}
}
pub fn sort(&mut self) -> &mut Self {
match self {
ParamIndices::Joint(_, _) => {}
ParamIndices::Disjoint(indices) => indices.sort(),
}
self
}
pub fn sorted(&self) -> Self {
match self {
ParamIndices::Joint(s, l) => ParamIndices::Joint(*s, *l),
ParamIndices::Disjoint(indices) => {
let mut indices_out = indices.clone();
indices_out.sort();
ParamIndices::Disjoint(indices_out)
}
}
}
pub fn iter<'a>(&'a self) -> ParamIndicesIter<'a> {
match self {
ParamIndices::Joint(start, length) => ParamIndicesIter::Joint {
start: *start,
length: *length,
current: 0,
},
ParamIndices::Disjoint(indices) => ParamIndicesIter::Disjoint {
indices,
current: 0,
},
}
}
pub fn contains(&self, index: usize) -> bool {
match self {
ParamIndices::Joint(start, length) => index >= *start && index < *start + *length,
ParamIndices::Disjoint(indices) => indices.contains(&index),
}
}
pub fn to_vec(self) -> Vec<usize> {
match self {
ParamIndices::Joint(_, _) => self.iter().collect(),
ParamIndices::Disjoint(vec) => vec,
}
}
pub fn as_vec(&self) -> Vec<usize> {
self.iter().collect()
}
pub fn len(&self) -> usize {
match self {
ParamIndices::Joint(_, length) => *length,
ParamIndices::Disjoint(indices) => indices.len(),
}
}
#[inline]
pub fn get(&self, index: usize) -> usize {
match self {
ParamIndices::Joint(start, length) => {
if index >= *length {
panic!(
"Index {} out of bounds for Joint range of length {}",
index, length
);
}
start + index
}
ParamIndices::Disjoint(v) => v[index],
}
}
pub fn map_through(&self, parent: &ParamIndices) -> ParamIndices {
match (self, parent) {
(ParamIndices::Joint(sub_start, sub_len), ParamIndices::Joint(p_start, p_len)) => {
if sub_start + sub_len > *p_len {
panic!("Sub-parameter index out of parent bounds");
}
ParamIndices::Joint(p_start + sub_start, *sub_len)
}
_ => {
let mapped: Vec<usize> =
self.iter().map(|local_idx| parent.get(local_idx)).collect();
ParamIndices::Disjoint(mapped)
}
}
}
}
pub enum ParamIndicesIter<'a> {
Joint {
start: usize,
length: usize,
current: usize,
},
Disjoint {
indices: &'a [usize],
current: usize,
},
}
impl<'a> Iterator for ParamIndicesIter<'a> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
match self {
ParamIndicesIter::Joint {
start,
length,
current,
} => {
if *current < *length {
let result = *start + *current;
*current += 1;
Some(result)
} else {
None
}
}
ParamIndicesIter::Disjoint { indices, current } => {
if *current < indices.len() {
let result = indices[*current];
*current += 1;
Some(result)
} else {
None
}
}
}
}
}
impl<T: AsRef<[usize]>> From<T> for ParamIndices {
fn from(indices: T) -> Self {
let indices = indices.as_ref();
if indices.is_empty() {
return ParamIndices::Joint(0, 0);
}
let first_idx = indices[0];
let mut is_consecutive = true;
for i in 1..indices.len() {
if indices[i] != first_idx + i {
is_consecutive = false;
break;
}
}
if is_consecutive {
ParamIndices::Joint(first_idx, indices.len())
} else {
ParamIndices::Disjoint(indices.to_vec())
}
}
}
impl<'a> IntoIterator for &'a ParamIndices {
type Item = usize;
type IntoIter = ParamIndicesIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ParamInfo {
indices: ParamIndices,
constant: Vec<bool>, }
impl ParamInfo {
pub fn new(indices: impl Into<ParamIndices>, constant: Vec<bool>) -> ParamInfo {
ParamInfo {
indices: indices.into(),
constant,
}
}
pub fn parameterized(indices: impl Into<ParamIndices>) -> ParamInfo {
let indices = indices.into();
let num_params = indices.num_params();
ParamInfo {
indices,
constant: vec![false; num_params],
}
}
pub fn to_sorted(&self) -> ParamInfo {
let index_to_constant_map: std::collections::HashMap<usize, bool> = self
.indices
.iter()
.zip(self.constant.iter().cloned())
.collect();
let new_indices = self.indices.sorted();
let new_constant: Vec<bool> = new_indices
.iter()
.map(|index| *index_to_constant_map.get(&index).unwrap_or(&false))
.collect();
ParamInfo {
indices: new_indices,
constant: new_constant,
}
}
pub fn union(&self, other: &ParamInfo) -> ParamInfo {
let combined_indices = self.indices.union(&other.indices);
let self_index_to_constant: std::collections::HashMap<usize, bool> = self
.indices
.iter()
.zip(self.constant.iter().cloned())
.collect();
let other_index_to_constant: std::collections::HashMap<usize, bool> = other
.indices
.iter()
.zip(other.constant.iter().cloned())
.collect();
let mut new_constant = Vec::new();
for index in combined_indices.iter() {
if let Some(&c) = self_index_to_constant.get(&index) {
if let Some(&c_other) = other_index_to_constant.get(&index) {
assert_eq!(c, c_other);
}
new_constant.push(c);
} else if let Some(&c) = other_index_to_constant.get(&index) {
new_constant.push(c);
} else {
unreachable!();
};
}
ParamInfo {
indices: combined_indices,
constant: new_constant,
}
}
pub fn intersect(&self, other: &ParamInfo) -> ParamInfo {
let combined_indices = self.indices.intersect(&other.indices);
let self_index_to_constant: std::collections::HashMap<usize, bool> = self
.indices
.iter()
.zip(self.constant.iter().cloned())
.collect();
let other_index_to_constant: std::collections::HashMap<usize, bool> = other
.indices
.iter()
.zip(other.constant.iter().cloned())
.collect();
let mut new_constant = Vec::new();
for index in combined_indices.iter() {
let c_self = self_index_to_constant.get(&index);
let c_other = other_index_to_constant.get(&index);
match (c_self, c_other) {
(Some(&s_const), Some(&o_const)) => {
assert_eq!(
s_const, o_const,
"Constant status mismatch for common index {}",
index
);
new_constant.push(s_const);
}
_ => unreachable!(
"Intersected index {} not found in both original ParamInfos",
index
),
}
}
ParamInfo {
indices: combined_indices,
constant: new_constant,
}
}
pub fn num_params(&self) -> usize {
self.indices.num_params()
}
pub fn num_var_params(&self) -> usize {
self.indices.num_params() - self.constant.iter().filter(|&x| *x).count()
}
pub fn get_param_map(&self) -> Vec<u64> {
self.indices.iter().map(|x| x as u64).collect()
}
pub fn get_const_map(&self) -> Vec<bool> {
self.constant.clone()
}
pub fn empty() -> ParamInfo {
ParamInfo {
indices: vec![].into(),
constant: vec![],
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.constant.len()
}
pub fn sorted_non_constant(&self) -> Vec<usize> {
let mut non_constant_indices = Vec::new();
for (index, &is_constant) in self.indices.iter().zip(self.constant.iter()) {
if !is_constant {
non_constant_indices.push(index);
}
}
non_constant_indices.sort();
non_constant_indices
}
}
pub trait HasParams {
fn num_params(&self) -> usize;
}
pub trait HasBounds<R: RealScalar>: HasParams {
fn bounds(&self) -> Vec<Range<R>>;
}
pub trait HasPeriods<R: RealScalar>: HasParams {
fn periods(&self) -> Vec<Range<R>>;
}
#[cfg(feature = "python")]
mod python {
use super::*;
use pyo3::prelude::*;
use pyo3::types::PyIterator;
use pyo3_stub_gen::derive::*;
use pyo3_stub_gen::impl_stub_type;
impl_stub_type!(ParamIndices = PyParamIndices);
#[gen_stub_pyclass]
#[pyclass(
name = "ParamIndices",
module = "openqudit",
frozen,
eq,
hash,
from_py_object
)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PyParamIndices {
inner: ParamIndices,
}
#[gen_stub_pymethods]
#[pymethods]
impl PyParamIndices {
#[new]
#[pyo3(signature = (indices_or_start, length = None))]
fn new<'py>(indices_or_start: &Bound<'py, PyAny>, length: Option<usize>) -> PyResult<Self> {
if let Some(len) = length {
let start: usize = indices_or_start.extract()?;
Ok(PyParamIndices {
inner: ParamIndices::Joint(start, len),
})
} else {
if let Ok(single_index) = indices_or_start.extract::<usize>() {
Ok(PyParamIndices {
inner: ParamIndices::Joint(single_index, 1),
})
} else {
let iter = PyIterator::from_object(indices_or_start)?;
let mut indices = Vec::new();
for item in iter {
let index: usize = item?.extract()?;
indices.push(index);
}
Ok(PyParamIndices {
inner: ParamIndices::Disjoint(indices),
})
}
}
}
#[getter]
fn num_params(&self) -> usize {
self.inner.num_params()
}
#[getter]
fn start(&self) -> usize {
self.inner.start()
}
#[getter]
fn is_consecutive(&self) -> bool {
self.inner.is_consecutive()
}
#[getter]
fn is_empty(&self) -> bool {
self.inner.is_empty()
}
fn contains(&self, index: usize) -> bool {
self.inner.contains(index)
}
fn to_list(&self) -> Vec<usize> {
self.inner.as_vec()
}
fn sorted(&self) -> PyParamIndices {
PyParamIndices {
inner: self.inner.sorted(),
}
}
fn union(&self, other: &PyParamIndices) -> PyParamIndices {
PyParamIndices {
inner: self.inner.union(&other.inner),
}
}
fn intersect(&self, other: &PyParamIndices) -> PyParamIndices {
PyParamIndices {
inner: self.inner.intersect(&other.inner),
}
}
#[staticmethod]
fn constant() -> PyParamIndices {
PyParamIndices {
inner: ParamIndices::empty(),
}
}
fn __repr__(&self) -> String {
match &self.inner {
ParamIndices::Joint(start, length) => {
if *length == 1 {
format!("ParamIndices({})", start)
} else {
format!("ParamIndices({}, {})", start, length)
}
}
ParamIndices::Disjoint(indices) => {
format!("ParamIndices({:?})", indices)
}
}
}
fn __str__(&self) -> String {
let indices = self.inner.as_vec();
if indices.len() <= 3 {
format!("{:?}", indices)
} else {
format!(
"[{}, ..., {}]",
indices.first().unwrap(),
indices.last().unwrap()
)
}
}
fn __len__(&self) -> usize {
self.inner.len()
}
fn __iter__(slf: PyRef<'_, Self>) -> PyParamIndicesIterator {
PyParamIndicesIterator {
indices: slf.inner.as_vec(),
index: 0,
}
}
fn __contains__(&self, index: usize) -> bool {
self.inner.contains(index)
}
}
#[gen_stub_pyclass]
#[pyclass(name = "ParamIndicesIterator", module = "openqudit")]
struct PyParamIndicesIterator {
indices: Vec<usize>,
index: usize,
}
#[gen_stub_pymethods]
#[pymethods]
impl PyParamIndicesIterator {
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<usize> {
if slf.index < slf.indices.len() {
let result = slf.indices[slf.index];
slf.index += 1;
Some(result)
} else {
None
}
}
}
impl From<ParamIndices> for PyParamIndices {
fn from(indices: ParamIndices) -> Self {
PyParamIndices { inner: indices }
}
}
impl From<PyParamIndices> for ParamIndices {
fn from(py_indices: PyParamIndices) -> Self {
py_indices.inner
}
}
impl<'py> IntoPyObject<'py> for ParamIndices {
type Target = PyParamIndices;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let py_indices = PyParamIndices::from(self);
Bound::new(py, py_indices)
}
}
impl<'a, 'py> FromPyObject<'a, 'py> for ParamIndices {
type Error = PyErr;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
let py_indices: PyParamIndices = ob.extract()?;
Ok(py_indices.into())
}
}
fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
parent_module.add_class::<PyParamIndices>()?;
parent_module.add_class::<PyParamIndicesIterator>()?;
Ok(())
}
inventory::submit! { crate::PyRegistrar { func: register } }
}