qudit_expr/expressions/
bra.rs

1use std::ops::Deref;
2use std::ops::DerefMut;
3
4use crate::GenerationShape;
5use crate::TensorExpression;
6use crate::expressions::JittableExpression;
7use crate::index::IndexDirection;
8use crate::index::TensorIndex;
9
10use super::NamedExpression;
11use qudit_core::QuditSystem;
12use qudit_core::Radices;
13
14#[derive(PartialEq, Eq, Debug, Clone)]
15pub struct BraExpression {
16    inner: NamedExpression,
17    radices: Radices,
18}
19
20impl BraExpression {
21    pub fn new<T: AsRef<str>>(input: T) -> Self {
22        TensorExpression::new(input).try_into().unwrap()
23    }
24}
25
26impl JittableExpression for BraExpression {
27    fn generation_shape(&self) -> GenerationShape {
28        GenerationShape::Vector(self.radices.dimension())
29    }
30}
31
32impl AsRef<NamedExpression> for BraExpression {
33    fn as_ref(&self) -> &NamedExpression {
34        &self.inner
35    }
36}
37
38impl From<BraExpression> for NamedExpression {
39    fn from(value: BraExpression) -> Self {
40        value.inner
41    }
42}
43
44impl Deref for BraExpression {
45    type Target = NamedExpression;
46
47    fn deref(&self) -> &Self::Target {
48        &self.inner
49    }
50}
51
52impl DerefMut for BraExpression {
53    fn deref_mut(&mut self) -> &mut Self::Target {
54        &mut self.inner
55    }
56}
57
58impl From<BraExpression> for TensorExpression {
59    fn from(value: BraExpression) -> Self {
60        let BraExpression { inner, radices } = value;
61        // TODO: add a proper implementation of into_iter for QuditRadices
62        let indices = radices
63            .iter()
64            .enumerate()
65            .map(|(i, r)| TensorIndex::new(IndexDirection::Input, i, usize::from(*r)))
66            .collect();
67        TensorExpression::from_raw(indices, inner)
68    }
69}
70
71impl TryFrom<TensorExpression> for BraExpression {
72    // TODO: Come up with proper error handling
73    type Error = String;
74
75    fn try_from(value: TensorExpression) -> Result<Self, Self::Error> {
76        if value
77            .indices()
78            .iter()
79            .any(|idx| idx.direction() != IndexDirection::Input)
80        {
81            return Err(String::from(
82                "Cannot convert a tensor with non-input indices to a bra.",
83            ));
84        }
85        let radices = Radices::from_iter(value.indices().iter().map(|idx| idx.index_size()));
86        Ok(BraExpression {
87            inner: value.into(),
88            radices,
89        })
90    }
91}
92
93impl QuditSystem for BraExpression {
94    fn radices(&self) -> Radices {
95        self.radices.clone()
96    }
97
98    fn num_qudits(&self) -> usize {
99        self.radices.num_qudits()
100    }
101}
102
103#[cfg(feature = "python")]
104mod python {
105    use super::*;
106    use crate::python::PyExpressionRegistrar;
107    use pyo3::prelude::*;
108    use qudit_core::Radix;
109
110    #[pyclass]
111    #[pyo3(name = "BraExpression")]
112    pub struct PyBraExpression {
113        expr: BraExpression,
114    }
115
116    #[pymethods]
117    impl PyBraExpression {
118        #[new]
119        fn new(expr: String) -> Self {
120            Self {
121                expr: BraExpression::new(expr),
122            }
123        }
124
125        fn num_params(&self) -> usize {
126            self.expr.num_params()
127        }
128
129        fn name(&self) -> String {
130            self.expr.name().to_string()
131        }
132
133        fn radices(&self) -> Vec<Radix> {
134            self.expr.radices().to_vec()
135        }
136
137        fn dimension(&self) -> usize {
138            self.expr.dimension()
139        }
140
141        fn __repr__(&self) -> String {
142            format!(
143                "BraExpression(name='{}', radices={:?}, params={})",
144                self.expr.name(),
145                self.expr.radices().to_vec(),
146                self.expr.num_params()
147            )
148        }
149    }
150
151    impl From<BraExpression> for PyBraExpression {
152        fn from(value: BraExpression) -> Self {
153            PyBraExpression { expr: value }
154        }
155    }
156
157    impl From<PyBraExpression> for BraExpression {
158        fn from(value: PyBraExpression) -> Self {
159            value.expr
160        }
161    }
162
163    impl<'py> IntoPyObject<'py> for BraExpression {
164        type Target = <PyBraExpression as IntoPyObject<'py>>::Target;
165        type Output = Bound<'py, Self::Target>;
166        type Error = PyErr;
167
168        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
169            let py_expr = PyBraExpression::from(self);
170            Bound::new(py, py_expr)
171        }
172    }
173
174    impl<'a, 'py> FromPyObject<'a, 'py> for BraExpression {
175        type Error = PyErr;
176
177        fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
178            let py_expr: PyRef<PyBraExpression> = ob.extract()?;
179            Ok(py_expr.expr.clone())
180        }
181    }
182
183    /// Registers the BraExpression class with the Python module.
184    fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
185        parent_module.add_class::<PyBraExpression>()?;
186        Ok(())
187    }
188    inventory::submit!(PyExpressionRegistrar { func: register });
189}