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// }