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