qudit_inst/
lib.rs

1mod instantiater;
2pub mod numerical;
3mod qfactor;
4mod result;
5mod target;
6
7pub use instantiater::DataMap;
8pub use instantiater::Instantiater;
9pub use result::InstantiationResult;
10pub use target::InstantiationTarget;
11
12#[cfg(feature = "python")]
13pub use instantiater::python::PyInstantiater;
14
15////////////////////////////////////////////////////////////////////////
16/// Python Module.
17////////////////////////////////////////////////////////////////////////
18#[cfg(feature = "python")]
19pub(crate) mod python {
20    use pyo3::prelude::{Bound, PyAnyMethods, PyModule, PyModuleMethods, PyResult};
21
22    /// A trait for objects that can register importables with a PyO3 module.
23    pub struct PyInstantiationRegistrar {
24        /// The registration function
25        pub func: fn(parent_module: &Bound<'_, PyModule>) -> PyResult<()>,
26    }
27
28    inventory::collect!(PyInstantiationRegistrar);
29
30    /// Registers the Circuit submodule with the Python module.
31    fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
32        let submodule = PyModule::new(parent_module.py(), "instantiation")?;
33
34        for registrar in inventory::iter::<PyInstantiationRegistrar> {
35            (registrar.func)(&submodule)?;
36        }
37
38        parent_module.add_submodule(&submodule)?;
39        parent_module
40            .py()
41            .import("sys")?
42            .getattr("modules")?
43            .set_item("openqudit.instantiation", submodule)?;
44
45        Ok(())
46    }
47
48    inventory::submit!(qudit_core::PyRegistrar { func: register });
49}
50////////////////////////////////////////////////////////////////////////
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use crate::numerical::MinimizingInstantiater;
56    use crate::numerical::functions::HSProblem;
57    // use crate::numerical::initializers::GreedyFurthestPoint;
58    use crate::numerical::initializers::Uniform;
59    // use crate::numerical::initializers::Zeros;
60    use crate::numerical::minimizers::LM;
61    use crate::numerical::runners::MultiStartRunner;
62    use faer::mat;
63    use qudit_circuit::ArgumentList;
64    use qudit_circuit::QuditCircuit;
65    use qudit_core::UnitaryMatrix;
66    // use qudit_core::c32;
67    use qudit_core::c64;
68    use qudit_expr::BraSystemExpression;
69    // use qudit_expr::FUNCTION;
70    // use qudit_expr::GRADIENT;
71    use qudit_expr::UnitaryExpression;
72    use qudit_expr::library::Controlled;
73    use qudit_expr::library::PGate;
74    use qudit_expr::library::U3Gate;
75    use qudit_expr::library::XGate;
76    // use qudit_tensor::TNVM;
77    use std::sync::Arc;
78
79    #[allow(dead_code)]
80    pub fn build_qsearch_thin_step_circuit(n: usize) -> QuditCircuit {
81        let block_expr = U3Gate()
82            .otimes(U3Gate())
83            .dot(Controlled(XGate(2), [2].into(), None));
84        let mut circ = QuditCircuit::pure(vec![2; n]);
85        for i in 0..n {
86            circ.append(U3Gate(), [i], None);
87        }
88        for _ in 0..2 {
89            for i in 0..(n - 1) {
90                circ.append(block_expr.clone(), [i, i + 1], None);
91            }
92        }
93        circ
94    }
95
96    #[allow(dead_code)]
97    pub fn build_qutrit_thin_step_circuit(n: usize) -> QuditCircuit {
98        let csum = UnitaryExpression::new(
99            "CSUM() {
100            [
101                [ 1, 0, 0, 0, 0, 0, 0, 0, 0 ],
102                [ 0, 1, 0, 0, 0, 0, 0, 0, 0 ],
103                [ 0, 0, 1, 0, 0, 0, 0, 0, 0 ],
104                [ 0, 0, 0, 0, 0, 1, 0, 0, 0 ],
105                [ 0, 0, 0, 1, 0, 0, 0, 0, 0 ],
106                [ 0, 0, 0, 0, 1, 0, 0, 0, 0 ],
107                [ 0, 0, 0, 0, 0, 0, 0, 1, 0 ],
108                [ 0, 0, 0, 0, 0, 0, 0, 0, 1 ],
109                [ 0, 0, 0, 0, 0, 0, 1, 0, 0 ],
110            ]
111        }",
112        );
113
114        let block_expr = PGate(3).otimes(PGate(3)).dot(csum);
115        let mut circ = QuditCircuit::pure(vec![3; n]);
116        for i in 0..n {
117            circ.append(PGate(3), [i], None);
118        }
119        for _ in 0..2 {
120            for i in 0..(n - 1) {
121                circ.append(block_expr.clone(), [i, i + 1], None);
122            }
123        }
124        circ
125    }
126
127    #[allow(dead_code)]
128    pub fn build_dynamic_circuit() -> QuditCircuit {
129        let mut circ: QuditCircuit = QuditCircuit::new([2, 2, 2, 2], [2, 2]);
130
131        circ.zero_initialize([1, 2]);
132
133        for i in 0..4 {
134            circ.append(qudit_expr::library::U3Gate(), [i], None);
135        }
136
137        let block_expr = U3Gate()
138            .otimes(U3Gate())
139            .dot(Controlled(XGate(2), [2].into(), None));
140        circ.append(block_expr.clone(), [1, 2], None);
141        circ.append(
142            block_expr.clone(),
143            [0, 1],
144            ArgumentList::new(vec![None::<f64>.try_into().unwrap(); 6]),
145        );
146        circ.append(block_expr.clone(), [2, 3], None);
147
148        let one_qubit_basis_measurement = BraSystemExpression::new(
149            "OneQMeasure() {
150            [
151                [[ 1, 0, ]],
152                [[ 0, 1, ]],
153            ]
154        }",
155        );
156
157        circ.append(one_qubit_basis_measurement.clone(), ([1], [0]), None);
158        circ.append(one_qubit_basis_measurement, ([2], [1]), None);
159
160        let u3_u3 = U3Gate().otimes(U3Gate());
161        circ.append(
162            UnitaryExpression::classically_multiplex(&[&u3_u3, &u3_u3, &u3_u3, &u3_u3], &[2, 2]),
163            ([0, 3], [0, 1]),
164            None,
165        );
166
167        //////// START CNOT TELEPORTATION
168        // circ.zero_initialize([1, 2]);
169        // circ.append(Gate::H(), [1], None);
170        // circ.append(Gate::CX(), [1, 2], None);
171        // circ.append(Gate::CX(), [0, 1], None);
172        // circ.append(Gate::CX(), [2, 3], None);
173        // circ.append(Gate::H(), [2], None);
174
175        // let one_qubit_basis_measurement = BraSystemExpression::new("OneQMeasure() {
176        //     [
177        //         [[ 1, 0, ]],
178        //         [[ 0, 1, ]],
179        //     ]
180        // }");
181
182        // circ.append_parameterized(Operation::TerminatingMeasurement(one_qubit_basis_measurement.clone()), ([1], [0]));
183        // circ.append_parameterized(Operation::TerminatingMeasurement(one_qubit_basis_measurement), ([2], [1]));
184
185        // circ.append(Gate::X(2).generate_expression().classically_control(&[1], &[2]), ([3], [0]), None);
186        // circ.append(Gate::Z(2).generate_expression().classically_control(&[1], &[2]), ([0], [1]), None);
187        //////// END CNOT TELEPORTATION
188        circ
189    }
190
191    #[test]
192    fn test_lm_minimization_simple() {
193        // create simple circuit
194        let circ = build_dynamic_circuit();
195        // let circ = build_qsearch_thin_step_circuit(3);
196
197        // sample target
198        // let network = circ.to_tensor_network();
199        // let code = qudit_tensor::compile_network(network);
200        // let mut tnvm = qudit_tensor::TNVM::<c32, GRADIENT>::new(&code);
201        // let result = tnvm.evaluate::<FUNCTION>(&vec![1.7; circ.num_params()]).get_fn_result().unpack_matrix();
202        // let target_utry = UnitaryMatrix::new(circ.radices(), result.to_owned());
203        // let target_utry = UnitaryMatrix::new([2], mat![
204        //     [c64::new(1.0, 0.0), c64::new(0.0, 0.0)],
205        //     [c64::new(0.0, 0.0), c64::new(1.0, 0.0)],
206        // ]);
207        let target_utry = UnitaryMatrix::new(
208            [2, 2],
209            mat![
210                [
211                    c64::new(1.0, 0.0),
212                    c64::new(0.0, 0.0),
213                    c64::new(0.0, 0.0),
214                    c64::new(0.0, 0.0)
215                ],
216                [
217                    c64::new(0.0, 0.0),
218                    c64::new(1.0, 0.0),
219                    c64::new(0.0, 0.0),
220                    c64::new(0.0, 0.0)
221                ],
222                [
223                    c64::new(0.0, 0.0),
224                    c64::new(0.0, 0.0),
225                    c64::new(0.0, 0.0),
226                    c64::new(1.0, 0.0)
227                ],
228                [
229                    c64::new(0.0, 0.0),
230                    c64::new(0.0, 0.0),
231                    c64::new(1.0, 0.0),
232                    c64::new(0.0, 0.0)
233                ],
234            ],
235        );
236        let target = InstantiationTarget::UnitaryMatrix(target_utry);
237
238        // build instantiater
239        let minimizer = LM::default();
240        // let initializer = Zeros::default();
241        let initializer = Uniform::default();
242        // let initializer = GreedyFurthestPoint::default();
243        let runner = MultiStartRunner {
244            minimizer,
245            guess_generator: initializer,
246            num_starts: 16,
247        };
248        let instantiater = MinimizingInstantiater::<_, HSProblem<f64>>::new(runner);
249        let data = std::collections::HashMap::new();
250
251        // call instantiater
252        let circ = Arc::new(circ);
253        let target = Arc::new(target);
254        let data = Arc::new(data);
255        let _result = instantiater.instantiate(circ.clone(), target.clone(), data.clone());
256        let mut success_times = vec![];
257        let mut failure_times = vec![];
258        let n = 1000;
259        for _ in 0..n {
260            let now = std::time::Instant::now();
261            let result = instantiater.instantiate(circ.clone(), target.clone(), data.clone());
262            let elapsed = now.elapsed();
263            if let Some(f) = result.fun {
264                // dbg!(&f);
265                // dbg!(result.message);
266                if f < 1e-4 {
267                    success_times.push(elapsed);
268                } else {
269                    failure_times.push(elapsed);
270                }
271            }
272        }
273        println!("Number of successes: {:?}", success_times.len());
274        println!("Number of failures: {:?}", failure_times.len());
275        if !success_times.is_empty() {
276            let average_success_time = success_times.iter().cloned().sum::<std::time::Duration>()
277                / (success_times.len() as u32);
278            println!("Average success time: {:?}", average_success_time);
279        }
280        if !failure_times.is_empty() {
281            let average_failure_time = failure_times.iter().cloned().sum::<std::time::Duration>()
282                / (failure_times.len() as u32);
283            println!("Average failure time: {:?}", average_failure_time);
284        }
285        let average_time = success_times
286            .iter()
287            .chain(failure_times.iter())
288            .cloned()
289            .sum::<std::time::Duration>()
290            / n;
291        println!("Average overall time: {:?}", average_time);
292    }
293}