qudit_inst/
result.rs

1use qudit_core::ComplexScalar;
2
3// pub type InstantiationResult = Result<InstantiationOutput, InstantiationError>;
4
5// pub struct InstantiationOutput<R: RealScalar> {
6//     params: Vec<R>,
7//     func: Option<R>,
8//     message: Option<String>,
9// }
10
11// pub struct InstantiationError {
12//     message: String
13// }
14
15/// Result of an instantiation operation.
16///
17/// This struct encapsulates the outcome of an instantiation attempt.
18/// It provides information about the success/failure status, and other
19/// relevant information. Very little is standardized between instantiaters,
20/// so refer to specific instantiaters for more documentation on how
21/// the fields are used.
22///
23/// A successful instantiation is one that was able to run until
24///
25/// # Status Codes
26/// * `0` - Successful termination
27/// * `1` - Input cannot be handled by instantiator
28/// * `2+` - Instantiator-specific error codes (see relevant documentation)
29#[derive(Clone, Debug, PartialEq)]
30pub struct InstantiationResult<C: ComplexScalar> {
31    /// The instantiated solution's parameters.
32    pub params: Option<Vec<C::R>>,
33
34    /// Optional function evaluation at the solution point.
35    ///
36    /// If provided, represents instantiation-specific objective function
37    /// value at the computed solution. Useful for assessing solution quality.
38    /// Consult with instantiater documentation.
39    pub fun: Option<C::R>,
40
41    /// Termination status code.
42    ///
43    /// - `0`: Successful termination
44    /// - `1`: Input cannot be handled by instantiator; see message
45    /// - `2+`: Instantiator-specific error codes; see relevant documentation
46    pub status: usize,
47
48    /// Optional diagnostic message, providing additional context.
49    pub message: Option<String>,
50}
51
52impl<C: ComplexScalar> InstantiationResult<C> {
53    /// Creates a new `InstantiationResult` with all fields specified.
54    ///
55    /// # Arguments
56    /// * `params` - Optional solution parameters
57    /// * `fun` - Optional function evaluation
58    /// * `status` - Status code (0 for success)
59    /// * `message` - Optional diagnostic message
60    ///
61    /// # Examples
62    ///
63    /// ```rust
64    /// # use qudit_inst::InstantiationResult;
65    /// # use qudit_core::c64;
66    ///
67    /// let result = InstantiationResult::<c64>::new(
68    ///     Some(vec![1.0, 2.0]),
69    ///     Some(0.1),
70    ///     0,
71    ///     Some("Converged successfully".to_string())
72    /// );
73    /// ```
74    pub fn new(
75        params: Option<Vec<C::R>>,
76        fun: Option<C::R>,
77        status: usize,
78        message: Option<String>,
79    ) -> Self {
80        Self {
81            params,
82            fun,
83            status,
84            message,
85        }
86    }
87
88    /// Creates a successful instantiation result.
89    ///
90    /// # Arguments
91    /// * `params` - The computed solution parameters
92    /// * `fun` - Optional function evaluation
93    ///
94    /// # Examples
95    ///
96    /// ```rust
97    /// # use qudit_inst::InstantiationResult;
98    /// # use qudit_core::c64;
99    ///
100    /// let result = InstantiationResult::<c64>::success(
101    ///     vec![0.5, 1.0, 1.5],
102    ///     Some(0.0001)
103    /// );
104    /// assert!(result.is_success());
105    /// ```
106    pub fn success(params: Vec<C::R>, fun: Option<C::R>) -> Self {
107        Self {
108            params: Some(params),
109            fun,
110            status: 0,
111            message: None,
112        }
113    }
114
115    /// Creates a successful instantiation result with a message.
116    ///
117    /// # Arguments
118    /// * `params` - The computed solution parameters
119    /// * `fun` - Optional function evaluation
120    /// * `message` - Success message
121    pub fn success_with_message(params: Vec<C::R>, fun: Option<C::R>, message: String) -> Self {
122        Self {
123            params: Some(params),
124            fun,
125            status: 0,
126            message: Some(message),
127        }
128    }
129
130    /// Returns true if the instantiation was successful (status == 0).
131    ///
132    /// # Examples
133    ///
134    /// ```rust
135    /// # use qudit_inst::InstantiationResult;
136    /// # use qudit_core::c64;
137    ///
138    /// let success = InstantiationResult::<c64>::success(vec![1.0], None);
139    /// assert!(success.is_success());
140    /// ```
141    pub fn is_success(&self) -> bool {
142        self.status == 0
143    }
144
145    /// Returns true if the instantiation failed (status > 0).
146    pub fn is_failure(&self) -> bool {
147        self.status > 0
148    }
149
150    /// Returns true if the result contains solution parameters.
151    pub fn has_params(&self) -> bool {
152        self.params.is_some()
153    }
154
155    /// Returns true if the result contains a function evaluation.
156    pub fn has_function_value(&self) -> bool {
157        self.fun.is_some()
158    }
159
160    /// Returns the number of parameters if available.
161    pub fn num_params(&self) -> Option<usize> {
162        self.params.as_ref().map(|p| p.len())
163    }
164}
165
166impl<C: ComplexScalar> std::fmt::Display for InstantiationResult<C>
167where
168    C::R: std::fmt::Display,
169{
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        write!(f, "InstantiationResult {{ status: {}", self.status)?;
172
173        if let Some(ref params) = self.params {
174            write!(f, ", params: [")?;
175            for (i, param) in params.iter().enumerate() {
176                if i > 0 {
177                    write!(f, ", ")?;
178                }
179                write!(f, "{}", param)?;
180            }
181            write!(f, "]")?;
182        }
183
184        if let Some(ref fun_val) = self.fun {
185            write!(f, ", fun: {}", fun_val)?;
186        }
187
188        if let Some(ref msg) = self.message {
189            write!(f, ", message: \"{}\"", msg)?;
190        }
191
192        write!(f, " }}")
193    }
194}
195
196#[cfg(feature = "python")]
197mod python {
198    use super::*;
199    use crate::python::PyInstantiationRegistrar;
200    use pyo3::prelude::*;
201    use qudit_core::c32;
202    use qudit_core::c64;
203
204    /// Python binding for InstantiationResult using f64 as the real number type.
205    ///
206    /// This provides a concrete instantiation of InstantiationResult for use in Python,
207    /// using f64 for real numbers (corresponding to Complex64 complex scalars).
208    #[pyclass(name = "InstantiationResult", frozen)]
209    #[derive(Clone, Debug)]
210    pub struct PyInstantiationResult {
211        inner: InstantiationResult<c64>,
212    }
213
214    #[pymethods]
215    impl PyInstantiationResult {
216        /// Creates a new InstantiationResult.
217        ///
218        /// Args:
219        ///     params: Optional list of solution parameters
220        ///     fun: Optional function evaluation value
221        ///     status: Status code (0 for success)
222        ///     message: Optional diagnostic message
223        ///
224        /// Returns:
225        ///     New InstantiationResult instance
226        #[new]
227        #[pyo3(signature = (params=None, fun=None, status=0, message=None))]
228        pub fn new(
229            params: Option<Vec<f64>>,
230            fun: Option<f64>,
231            status: usize,
232            message: Option<String>,
233        ) -> Self {
234            Self {
235                inner: InstantiationResult::new(params, fun, status, message),
236            }
237        }
238
239        /// Creates a successful instantiation result.
240        ///
241        /// Args:
242        ///     params: The computed solution parameters
243        ///     fun: Optional function evaluation
244        ///
245        /// Returns:
246        ///     Successful InstantiationResult
247        #[staticmethod]
248        #[pyo3(signature = (params, fun=None))]
249        pub fn success(params: Vec<f64>, fun: Option<f64>) -> Self {
250            Self {
251                inner: InstantiationResult::success(params, fun),
252            }
253        }
254
255        /// Creates a failed instantiation result.
256        ///
257        /// Args:
258        ///     status: Error status code (must be > 0)
259        ///     message: Optional error message
260        ///
261        /// Returns:
262        ///     Failed InstantiationResult
263        #[staticmethod]
264        #[pyo3(signature = (status, message=None))]
265        pub fn failure(status: usize, message: Option<String>) -> Self {
266            Self {
267                inner: InstantiationResult::new(None, None, status, message),
268            }
269        }
270
271        /// The instantiated solution parameters
272        #[getter]
273        pub fn params(&self) -> Option<Vec<f64>> {
274            self.inner.params.clone()
275        }
276
277        /// Optional function evaluation
278        #[getter]
279        pub fn fun(&self) -> Option<f64> {
280            self.inner.fun
281        }
282
283        /// Termination status code
284        #[getter]
285        pub fn status(&self) -> usize {
286            self.inner.status
287        }
288
289        /// Optional message
290        #[getter]
291        pub fn message(&self) -> Option<String> {
292            self.inner.message.clone()
293        }
294
295        /// Returns True if the instantiation was successful.
296        pub fn is_success(&self) -> bool {
297            self.inner.is_success()
298        }
299
300        /// Returns True if the instantiation failed.
301        pub fn is_failure(&self) -> bool {
302            self.inner.is_failure()
303        }
304
305        /// Returns True if the result contains solution parameters.
306        pub fn has_params(&self) -> bool {
307            self.inner.has_params()
308        }
309
310        /// Returns True if the result contains a function evaluation.
311        pub fn has_function_value(&self) -> bool {
312            self.inner.has_function_value()
313        }
314
315        /// Returns the number of parameters if available.
316        pub fn num_params(&self) -> Option<usize> {
317            self.inner.num_params()
318        }
319
320        /// String representation of the result.
321        pub fn __repr__(&self) -> String {
322            format!(
323                "InstantiationResult(status={}, params={:?}, fun={:?}, message={:?})",
324                self.inner.status, self.inner.params, self.inner.fun, self.inner.message
325            )
326        }
327
328        /// String representation of the result.
329        pub fn __str__(&self) -> String {
330            format!("{}", self.inner)
331        }
332    }
333
334    impl From<InstantiationResult<c32>> for PyInstantiationResult {
335        fn from(result: InstantiationResult<c32>) -> Self {
336            // Convert c32 result to c64 result
337            Self {
338                inner: InstantiationResult::new(
339                    result
340                        .params
341                        .map(|p| p.into_iter().map(|x| x as f64).collect()),
342                    result.fun.map(|x| x as f64),
343                    result.status,
344                    result.message,
345                ),
346            }
347        }
348    }
349
350    impl From<InstantiationResult<c64>> for PyInstantiationResult {
351        fn from(result: InstantiationResult<c64>) -> Self {
352            Self { inner: result }
353        }
354    }
355
356    impl From<PyInstantiationResult> for InstantiationResult<c64> {
357        fn from(py_result: PyInstantiationResult) -> Self {
358            py_result.inner
359        }
360    }
361
362    impl<'py> IntoPyObject<'py> for InstantiationResult<c64> {
363        type Target = PyInstantiationResult;
364        type Output = Bound<'py, PyInstantiationResult>;
365        type Error = PyErr;
366        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
367            let py_result = PyInstantiationResult::from(self);
368            Bound::new(py, py_result)
369        }
370    }
371
372    impl<'py> IntoPyObject<'py> for InstantiationResult<c32> {
373        type Target = PyInstantiationResult;
374        type Output = Bound<'py, PyInstantiationResult>;
375        type Error = PyErr;
376        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
377            let py_result = PyInstantiationResult::from(self);
378            Bound::new(py, py_result)
379        }
380    }
381
382    impl<'a, 'py> FromPyObject<'a, 'py> for InstantiationResult<c64> {
383        type Error = PyErr;
384
385        fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
386            let py_result: PyInstantiationResult = obj.extract()?;
387            Ok(py_result.into())
388        }
389    }
390
391    /// Registers the InstantiationResult class with the Python module.
392    fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
393        parent_module.add_class::<PyInstantiationResult>()?;
394        Ok(())
395    }
396    inventory::submit!(PyInstantiationRegistrar { func: register });
397}
398
399// #[cfg(test)]
400// mod tests {
401//     use super::*;
402//     use qudit_core::c64;
403
404//     type TestResult = InstantiationResult<c64>;
405
406//     #[test]
407//     fn test_new() {
408//         let result = TestResult::new(
409//             Some(vec![1.0, 2.0, 3.0]),
410//             Some(0.5),
411//             0,
412//             Some("Success".to_string())
413//         );
414
415//         assert_eq!(result.params, Some(vec![1.0, 2.0, 3.0]));
416//         assert_eq!(result.fun, Some(0.5));
417//         assert_eq!(result.status, 0);
418//         assert_eq!(result.message, Some("Success".to_string()));
419//     }
420
421//     #[test]
422//     fn test_success() {
423//         let result = TestResult::success(vec![1.0, 2.0], Some(0.1));
424
425//         assert!(result.is_success());
426//         assert!(!result.is_failure());
427//         assert!(result.has_params());
428//         assert!(result.has_function_value());
429//         assert_eq!(result.param_count(), Some(2));
430//         assert_eq!(result.status, 0);
431//     }
432
433//     #[test]
434//     fn test_success_with_message() {
435//         let result = TestResult::success_with_message(
436//             vec![1.0],
437//             None,
438//             "Converged in 10 iterations".to_string()
439//         );
440
441//         assert!(result.is_success());
442//         assert_eq!(result.message, Some("Converged in 10 iterations".to_string()));
443//     }
444
445//     #[test]
446//     fn test_failure() {
447//         let result = TestResult::failure(1, Some("Invalid input".to_string()));
448
449//         assert!(result.is_failure());
450//         assert!(!result.is_success());
451//         assert!(!result.has_params());
452//         assert!(!result.has_function_value());
453//         assert_eq!(result.param_count(), None);
454//         assert_eq!(result.status, 1);
455//         assert_eq!(result.message, Some("Invalid input".to_string()));
456//     }
457
458//     #[test]
459//     fn test_into_params() {
460//         let params = vec![1.0, 2.0, 3.0];
461//         let result = TestResult::success(params.clone(), None);
462
463//         assert_eq!(result.into_params(), Some(params));
464//     }
465
466//     #[test]
467//     fn test_params_ref() {
468//         let params = vec![1.0, 2.0, 3.0];
469//         let result = TestResult::success(params.clone(), None);
470
471//         assert_eq!(result.params_ref(), Some(params.as_slice()));
472//     }
473
474//     #[test]
475//     fn test_map_params() {
476//         let result = TestResult::success(vec![1.0, 2.0], None);
477//         let mapped = result.map_params(|params| params.iter().map(|&x| x * 2.0).collect());
478
479//         assert_eq!(mapped.params_ref(), Some(&[2.0, 4.0][..]));
480//     }
481
482//     #[test]
483//     fn test_map_params_no_params() {
484//         let result = TestResult::failure(1, None);
485//         let mapped = result.map_params(|params| params.iter().map(|&x| x * 2.0).collect());
486
487//         assert_eq!(mapped.params_ref(), None);
488//     }
489
490//     #[test]
491//     fn test_display() {
492//         let result = TestResult::success(vec![1.5, 2.5], Some(0.1));
493//         let display_str = format!("{}", result);
494
495//         assert!(display_str.contains("status: 0"));
496//         assert!(display_str.contains("params: [1.5, 2.5]"));
497//         assert!(display_str.contains("fun: 0.1"));
498//     }
499
500//     #[test]
501//     fn test_display_failure() {
502//         let result = TestResult::failure(1, Some("Error".to_string()));
503//         let display_str = format!("{}", result);
504
505//         assert!(display_str.contains("status: 1"));
506//         assert!(display_str.contains("message: \"Error\""));
507//         assert!(!display_str.contains("params:"));
508//         assert!(!display_str.contains("fun:"));
509//     }
510
511//     #[test]
512//     fn test_default() {
513//         let result = TestResult::default();
514
515//         assert!(result.is_failure());
516//         assert_eq!(result.status, 1);
517//         assert!(result.message.is_some());
518//     }
519
520//     #[test]
521//     fn test_clone_and_debug() {
522//         let original = TestResult::success(vec![1.0], None);
523//         let cloned = original.clone();
524
525//         assert_eq!(original, cloned);
526
527//         let debug_str = format!("{:?}", original);
528//         assert!(debug_str.contains("InstantiationResult"));
529//     }
530
531//     #[cfg(feature = "python")]
532//     mod python_tests {
533//         use super::super::python::*;
534
535//         #[test]
536//         fn test_py_instantiation_result_new() {
537//             let result = PyInstantiationResult::new(
538//                 Some(vec![1.0, 2.0]),
539//                 Some(0.5),
540//                 0,
541//                 Some("Success".to_string())
542//             );
543
544//             assert_eq!(result.params, Some(vec![1.0, 2.0]));
545//             assert_eq!(result.fun, Some(0.5));
546//             assert_eq!(result.status, 0);
547//         }
548
549//         #[test]
550//         fn test_py_success() {
551//             let result = PyInstantiationResult::success(vec![1.0, 2.0], Some(0.1));
552
553//             assert!(result.is_success());
554//             assert!(!result.is_failure());
555//             assert!(result.has_params());
556//         }
557
558//         #[test]
559//         fn test_py_failure() {
560//             let result = PyInstantiationResult::failure(1, Some("Error".to_string()));
561
562//             assert!(result.is_failure());
563//             assert!(!result.is_success());
564//             assert!(!result.has_params());
565//         }
566
567//         #[test]
568//         fn test_py_conversion_from_rust() {
569//             let rust_result = super::TestResult::success(vec![1.0, 2.0], Some(0.5));
570//             let py_result: PyInstantiationResult = rust_result.into();
571
572//             assert_eq!(py_result.params, Some(vec![1.0, 2.0]));
573//             assert_eq!(py_result.fun, Some(0.5));
574//             assert_eq!(py_result.status, 0);
575//         }
576
577//         #[test]
578//         fn test_py_conversion_to_rust() {
579//             let py_result = PyInstantiationResult::success(vec![1.0, 2.0], Some(0.5));
580//             let rust_result: super::TestResult = py_result.into();
581
582//             assert_eq!(rust_result.params, Some(vec![1.0, 2.0]));
583//             assert_eq!(rust_result.fun, Some(0.5));
584//             assert_eq!(rust_result.status, 0);
585//         }
586//     }
587// }