Skip to main content

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 pyo3_stub_gen::derive::*;
109    use qudit_core::Radix;
110
111    /// A symbolic bra (row) vector expression over a qudit system.
112    #[gen_stub_pyclass]
113    #[pyclass(name = "BraExpression", module = "openqudit.expressions")]
114    pub struct PyBraExpression {
115        expr: BraExpression,
116    }
117
118    #[gen_stub_pymethods]
119    #[pymethods]
120    impl PyBraExpression {
121        /// Parses a bra expression from its string representation.
122        ///
123        /// # Arguments
124        ///
125        /// * `expr` - The textual definition of the bra expression.
126        #[new]
127        fn new(expr: String) -> Self {
128            Self {
129                expr: BraExpression::new(expr),
130            }
131        }
132
133        /// Returns the number of free (unbound) parameters in this expression.
134        fn num_params(&self) -> usize {
135            self.expr.num_params()
136        }
137
138        /// Returns the name assigned to this expression.
139        fn name(&self) -> String {
140            self.expr.name().to_string()
141        }
142
143        /// Returns the radix of each qudit that this bra acts on.
144        fn radices(&self) -> Vec<Radix> {
145            self.expr.radices().to_vec()
146        }
147
148        /// Returns the total Hilbert space dimension of the underlying qudit system.
149        fn dimension(&self) -> usize {
150            self.expr.dimension()
151        }
152
153        fn __repr__(&self) -> String {
154            format!(
155                "BraExpression(name='{}', radices={:?}, params={})",
156                self.expr.name(),
157                self.expr.radices().to_vec(),
158                self.expr.num_params()
159            )
160        }
161    }
162
163    impl From<BraExpression> for PyBraExpression {
164        fn from(value: BraExpression) -> Self {
165            PyBraExpression { expr: value }
166        }
167    }
168
169    impl From<PyBraExpression> for BraExpression {
170        fn from(value: PyBraExpression) -> Self {
171            value.expr
172        }
173    }
174
175    impl<'py> IntoPyObject<'py> for BraExpression {
176        type Target = <PyBraExpression as IntoPyObject<'py>>::Target;
177        type Output = Bound<'py, Self::Target>;
178        type Error = PyErr;
179
180        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
181            let py_expr = PyBraExpression::from(self);
182            Bound::new(py, py_expr)
183        }
184    }
185
186    impl<'a, 'py> FromPyObject<'a, 'py> for BraExpression {
187        type Error = PyErr;
188
189        fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
190            let py_expr: PyRef<PyBraExpression> = ob.extract()?;
191            Ok(py_expr.expr.clone())
192        }
193    }
194
195    /// Registers the BraExpression class with the Python module.
196    fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
197        parent_module.add_class::<PyBraExpression>()?;
198        Ok(())
199    }
200    inventory::submit!(PyExpressionRegistrar { func: register });
201}