qudit_expr/expressions/
utrysys.rs1use std::ops::{Deref, DerefMut};
2
3use crate::{
4 GenerationShape, TensorExpression,
5 expressions::JittableExpression,
6 index::{IndexDirection, TensorIndex},
7};
8
9use super::NamedExpression;
10use qudit_core::QuditSystem;
11use qudit_core::Radices;
12
13#[derive(PartialEq, Eq, Debug, Clone)]
14pub struct UnitarySystemExpression {
15 inner: NamedExpression,
16 radices: Radices,
17 num_unitaries: usize,
18}
19
20impl UnitarySystemExpression {
21 pub fn new<T: AsRef<str>>(input: T) -> Self {
22 TensorExpression::new(input).try_into().unwrap()
23 }
24
25 pub fn num_qudits(&self) -> usize {
26 self.radices.num_qudits()
27 }
28}
29
30impl JittableExpression for UnitarySystemExpression {
31 fn generation_shape(&self) -> GenerationShape {
32 GenerationShape::Tensor3D(
33 self.num_unitaries,
34 self.radices.dimension(),
35 self.radices.dimension(),
36 )
37 }
38}
39
40impl AsRef<NamedExpression> for UnitarySystemExpression {
41 fn as_ref(&self) -> &NamedExpression {
42 &self.inner
43 }
44}
45
46impl From<UnitarySystemExpression> for NamedExpression {
47 fn from(value: UnitarySystemExpression) -> Self {
48 value.inner
49 }
50}
51
52impl Deref for UnitarySystemExpression {
53 type Target = NamedExpression;
54
55 fn deref(&self) -> &Self::Target {
56 &self.inner
57 }
58}
59
60impl DerefMut for UnitarySystemExpression {
61 fn deref_mut(&mut self) -> &mut Self::Target {
62 &mut self.inner
63 }
64}
65
66impl From<UnitarySystemExpression> for TensorExpression {
67 fn from(value: UnitarySystemExpression) -> Self {
68 let UnitarySystemExpression {
69 inner,
70 radices,
71 num_unitaries,
72 } = value;
73 let indices = [num_unitaries]
75 .into_iter()
76 .map(|r| (IndexDirection::Batch, r))
77 .chain(
78 radices
79 .iter()
80 .map(|r| (IndexDirection::Output, usize::from(*r))),
81 )
82 .chain(
83 radices
84 .iter()
85 .map(|r| (IndexDirection::Input, usize::from(*r))),
86 )
87 .enumerate()
88 .map(|(i, (d, r))| TensorIndex::new(d, i, r))
89 .collect();
90 TensorExpression::from_raw(indices, inner)
91 }
92}
93
94impl TryFrom<TensorExpression> for UnitarySystemExpression {
95 type Error = String;
97
98 fn try_from(value: TensorExpression) -> Result<Self, Self::Error> {
99 let mut num_unitaries = None;
100 let mut input_radices = vec![];
101 let mut output_radices = vec![];
102 for idx in value.indices() {
103 match idx.direction() {
104 IndexDirection::Batch => match &mut num_unitaries {
105 Some(n) => *n *= idx.index_size(),
106 None => num_unitaries = Some(idx.index_size()),
107 },
108 IndexDirection::Input => {
109 input_radices.push(idx.index_size());
110 }
111 IndexDirection::Output => {
112 output_radices.push(idx.index_size());
113 }
114 _ => unreachable!(),
115 }
116 }
117
118 if input_radices != output_radices {
119 return Err(String::from(
120 "Non-square matrix tensor cannot be converted to a unitary.",
121 ));
122 }
123
124 Ok(UnitarySystemExpression {
125 inner: value.into(),
126 radices: input_radices.into(),
127 num_unitaries: num_unitaries.unwrap_or(1),
128 })
129 }
130}
131
132#[cfg(feature = "python")]
133mod python {
134 use super::*;
135 use crate::python::PyExpressionRegistrar;
136 use pyo3::prelude::*;
137 use pyo3_stub_gen::derive::*;
138 use pyo3_stub_gen::impl_stub_type;
139 use qudit_core::Radix;
140
141 #[gen_stub_pyclass]
143 #[pyclass(name = "UnitarySystemExpression", module = "openqudit.expressions")]
144 pub struct PyUnitarySystemExpression {
145 expr: UnitarySystemExpression,
146 }
147
148 #[gen_stub_pymethods]
149 #[pymethods]
150 impl PyUnitarySystemExpression {
151 #[new]
157 fn new(expr: String) -> Self {
158 Self {
159 expr: UnitarySystemExpression::new(expr),
160 }
161 }
162
163 fn num_params(&self) -> usize {
165 self.expr.num_params()
166 }
167
168 fn name(&self) -> String {
170 self.expr.name().to_string()
171 }
172
173 fn radices(&self) -> Vec<Radix> {
175 self.expr.radices.to_vec()
176 }
177
178 fn num_qudits(&self) -> usize {
180 self.expr.num_qudits()
181 }
182
183 fn num_unitaries(&self) -> usize {
185 self.expr.num_unitaries
186 }
187
188 fn dimension(&self) -> usize {
190 self.expr.radices.dimension()
191 }
192
193 fn __repr__(&self) -> String {
194 format!(
195 "UnitarySystemExpression(name='{}', radices={:?}, num_unitaries={}, params={})",
196 self.expr.name(),
197 self.expr.radices.to_vec(),
198 self.expr.num_unitaries,
199 self.expr.num_params()
200 )
201 }
202 }
203
204 impl From<UnitarySystemExpression> for PyUnitarySystemExpression {
205 fn from(value: UnitarySystemExpression) -> Self {
206 PyUnitarySystemExpression { expr: value }
207 }
208 }
209
210 impl From<PyUnitarySystemExpression> for UnitarySystemExpression {
211 fn from(value: PyUnitarySystemExpression) -> Self {
212 value.expr
213 }
214 }
215
216 impl_stub_type!(UnitarySystemExpression = PyUnitarySystemExpression);
217
218 impl<'py> IntoPyObject<'py> for UnitarySystemExpression {
219 type Target = <PyUnitarySystemExpression as IntoPyObject<'py>>::Target;
220 type Output = Bound<'py, Self::Target>;
221 type Error = PyErr;
222
223 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
224 let py_expr = PyUnitarySystemExpression::from(self);
225 Bound::new(py, py_expr)
226 }
227 }
228
229 impl<'a, 'py> FromPyObject<'a, 'py> for UnitarySystemExpression {
230 type Error = PyErr;
231
232 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
233 let py_expr: PyRef<PyUnitarySystemExpression> = ob.extract()?;
234 Ok(py_expr.expr.clone())
235 }
236 }
237
238 fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
240 parent_module.add_class::<PyUnitarySystemExpression>()?;
241 Ok(())
242 }
243 inventory::submit!(PyExpressionRegistrar { func: register });
244}