1use std::collections::HashMap;
7use thiserror::Error;
8
9#[derive(Debug, Clone)]
11pub struct SparseVector<T> {
12 data: HashMap<usize, T>,
13 size: usize,
14}
15
16impl<T: Clone + Default + PartialEq> SparseVector<T> {
17 #[must_use]
18 pub fn new(size: usize) -> Self {
19 Self {
20 data: HashMap::new(),
21 size,
22 }
23 }
24
25 #[must_use]
26 pub fn get(&self, index: usize) -> Option<&T> {
27 self.data.get(&index)
28 }
29
30 pub fn set(&mut self, index: usize, value: T) {
31 if index < self.size {
32 if value == T::default() {
33 self.data.remove(&index);
34 } else {
35 self.data.insert(index, value);
36 }
37 }
38 }
39
40 pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> + '_ {
41 self.data.iter().map(|(&k, v)| (k, v))
42 }
43
44 pub fn remove(&mut self, index: usize) -> Option<T> {
45 self.data.remove(&index)
46 }
47
48 #[must_use]
49 pub fn nnz(&self) -> usize {
50 self.data.len()
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct CooMatrix<T> {
57 data: HashMap<(usize, usize), T>,
58 rows: usize,
59 cols: usize,
60}
61
62impl<T: Clone + Default + PartialEq> CooMatrix<T> {
63 #[must_use]
64 pub fn new(rows: usize, cols: usize) -> Self {
65 Self {
66 data: HashMap::new(),
67 rows,
68 cols,
69 }
70 }
71
72 #[must_use]
73 pub fn get(&self, row: usize, col: usize) -> Option<&T> {
74 self.data.get(&(row, col))
75 }
76
77 pub fn set(&mut self, row: usize, col: usize, value: T) {
78 if row < self.rows && col < self.cols {
79 if value == T::default() {
80 self.data.remove(&(row, col));
81 } else {
82 self.data.insert((row, col), value);
83 }
84 }
85 }
86
87 pub fn iter(&self) -> impl Iterator<Item = (usize, usize, &T)> + '_ {
88 self.data.iter().map(|(&(i, j), v)| (i, j, v))
89 }
90
91 #[must_use]
92 pub fn nnz(&self) -> usize {
93 self.data.len()
94 }
95}
96
97#[derive(Error, Debug, Clone)]
99pub enum IsingError {
100 #[error("Invalid qubit index: {0}")]
102 InvalidQubit(usize),
103
104 #[error("Invalid coupling between qubits {0} and {1}")]
106 InvalidCoupling(usize, usize),
107
108 #[error("Invalid value: {0}")]
110 InvalidValue(String),
111
112 #[error("Model exceeds hardware constraints: {0}")]
114 HardwareConstraint(String),
115}
116
117pub type IsingResult<T> = Result<T, IsingError>;
119
120#[derive(Debug, Clone, Copy, PartialEq)]
122pub struct Coupling {
123 pub i: usize,
125 pub j: usize,
127 pub strength: f64,
129}
130
131impl Coupling {
132 pub fn new(i: usize, j: usize, strength: f64) -> IsingResult<Self> {
134 if i == j {
135 return Err(IsingError::InvalidCoupling(i, j));
136 }
137
138 if !strength.is_finite() {
139 return Err(IsingError::InvalidValue(format!(
140 "Coupling strength must be finite, got {strength}"
141 )));
142 }
143
144 if i < j {
146 Ok(Self { i, j, strength })
147 } else {
148 Ok(Self {
149 i: j,
150 j: i,
151 strength,
152 })
153 }
154 }
155
156 #[must_use]
158 pub const fn involves(&self, qubit: usize) -> bool {
159 self.i == qubit || self.j == qubit
160 }
161}
162
163#[derive(Debug, Clone)]
173pub struct IsingModel {
174 pub num_qubits: usize,
176
177 biases: SparseVector<f64>,
179
180 couplings: CooMatrix<f64>,
182}
183
184impl IsingModel {
185 #[must_use]
187 pub fn new(num_qubits: usize) -> Self {
188 Self {
189 num_qubits,
190 biases: SparseVector::new(num_qubits),
191 couplings: CooMatrix::new(num_qubits, num_qubits),
192 }
193 }
194
195 pub fn set_bias(&mut self, qubit: usize, bias: f64) -> IsingResult<()> {
197 if qubit >= self.num_qubits {
198 return Err(IsingError::InvalidQubit(qubit));
199 }
200
201 if !bias.is_finite() {
202 return Err(IsingError::InvalidValue(format!(
203 "Bias must be finite, got {bias}"
204 )));
205 }
206
207 self.biases.set(qubit, bias);
208 Ok(())
209 }
210
211 pub fn get_bias(&self, qubit: usize) -> IsingResult<f64> {
213 if qubit >= self.num_qubits {
214 return Err(IsingError::InvalidQubit(qubit));
215 }
216
217 Ok(*self.biases.get(qubit).unwrap_or(&0.0))
218 }
219
220 pub fn set_coupling(&mut self, i: usize, j: usize, strength: f64) -> IsingResult<()> {
222 if i >= self.num_qubits || j >= self.num_qubits {
223 return Err(IsingError::InvalidQubit(std::cmp::max(i, j)));
224 }
225
226 if i == j {
227 return Err(IsingError::InvalidCoupling(i, j));
228 }
229
230 if !strength.is_finite() {
231 return Err(IsingError::InvalidValue(format!(
232 "Coupling strength must be finite, got {strength}"
233 )));
234 }
235
236 let (i, j) = if i < j { (i, j) } else { (j, i) };
238 self.couplings.set(i, j, strength);
239 Ok(())
240 }
241
242 pub fn get_coupling(&self, i: usize, j: usize) -> IsingResult<f64> {
244 if i >= self.num_qubits || j >= self.num_qubits {
245 return Err(IsingError::InvalidQubit(std::cmp::max(i, j)));
246 }
247
248 if i == j {
249 return Err(IsingError::InvalidCoupling(i, j));
250 }
251
252 let (i, j) = if i < j { (i, j) } else { (j, i) };
254 Ok(*self.couplings.get(i, j).unwrap_or(&0.0))
255 }
256
257 #[must_use]
259 pub fn biases(&self) -> Vec<(usize, f64)> {
260 self.biases
261 .iter()
262 .map(|(qubit, bias)| (qubit, *bias))
263 .collect()
264 }
265
266 #[must_use]
268 pub fn couplings(&self) -> Vec<Coupling> {
269 self.couplings
270 .iter()
271 .map(|(i, j, strength)| Coupling {
272 i,
273 j,
274 strength: *strength,
275 })
276 .collect()
277 }
278
279 pub fn energy(&self, spins: &[i8]) -> IsingResult<f64> {
286 if spins.len() != self.num_qubits {
287 return Err(IsingError::InvalidValue(format!(
288 "Spin configuration must have {} elements, got {}",
289 self.num_qubits,
290 spins.len()
291 )));
292 }
293
294 for (i, &spin) in spins.iter().enumerate() {
296 if spin != -1 && spin != 1 {
297 return Err(IsingError::InvalidValue(format!(
298 "Spin values must be -1 or 1, got {spin} at index {i}"
299 )));
300 }
301 }
302
303 let bias_energy: f64 = self
305 .biases
306 .iter()
307 .map(|(qubit, bias)| *bias * f64::from(spins[qubit]))
308 .sum();
309
310 let coupling_energy: f64 = self
312 .couplings
313 .iter()
314 .map(|(i, j, strength)| *strength * f64::from(spins[i]) * f64::from(spins[j]))
315 .sum();
316
317 Ok(bias_energy + coupling_energy)
318 }
319
320 #[must_use]
327 pub fn to_qubo(&self) -> QuboModel {
328 let mut qubo = QuboModel::new(self.num_qubits);
330
331 let mut linear_terms = HashMap::new();
333
334 for (i, j, coupling) in self.couplings.iter() {
336 let quadratic_term = 4.0 * *coupling;
338 let _ = qubo.set_quadratic(i, j, quadratic_term);
339
340 *linear_terms.entry(i).or_insert(0.0) -= 2.0 * *coupling;
342 *linear_terms.entry(j).or_insert(0.0) -= 2.0 * *coupling;
343 }
344
345 for (i, bias) in self.biases.iter() {
347 let linear_adj = *linear_terms.get(&i).unwrap_or(&0.0);
348 let linear_term = 2.0f64.mul_add(*bias, linear_adj);
349 let _ = qubo.set_linear(i, linear_term);
350 }
351
352 for (i, adj) in linear_terms {
354 let has_bias = self.biases.get(i).unwrap_or(&0.0).abs() > 1e-10;
355 if !has_bias {
356 let _ = qubo.set_linear(i, adj);
357 }
358 }
359
360 let coupling_sum: f64 = self
362 .couplings
363 .iter()
364 .map(|(_, _, strength)| *strength)
365 .sum();
366 qubo.offset = -coupling_sum;
367
368 qubo
369 }
370
371 #[must_use]
373 pub fn from_qubo(qubo: &QuboModel) -> Self {
374 let mut ising = Self::new(qubo.num_variables);
376
377 for (i, linear) in qubo.linear_terms.iter() {
379 let bias = *linear / 2.0;
381 let _ = ising.set_bias(i, bias);
383 }
384
385 for (i, j, quadratic) in qubo.quadratic_terms.iter() {
387 let coupling = *quadratic / 4.0;
389 let _ = ising.set_coupling(i, j, coupling);
391 }
392
393 ising
394 }
395}
396
397impl Default for IsingModel {
399 fn default() -> Self {
400 Self::new(0)
401 }
402}
403
404#[derive(Debug, Clone)]
411pub struct QuboModel {
412 pub num_variables: usize,
414
415 linear_terms: SparseVector<f64>,
417
418 quadratic_terms: CooMatrix<f64>,
420
421 pub offset: f64,
423}
424
425impl QuboModel {
426 #[must_use]
428 pub fn new(num_variables: usize) -> Self {
429 Self {
430 num_variables,
431 linear_terms: SparseVector::new(num_variables),
432 quadratic_terms: CooMatrix::new(num_variables, num_variables),
433 offset: 0.0,
434 }
435 }
436
437 pub fn set_linear(&mut self, var: usize, value: f64) -> IsingResult<()> {
439 if var >= self.num_variables {
440 return Err(IsingError::InvalidQubit(var));
441 }
442
443 if !value.is_finite() {
444 return Err(IsingError::InvalidValue(format!(
445 "Linear term must be finite, got {value}"
446 )));
447 }
448
449 self.linear_terms.set(var, value);
450 Ok(())
451 }
452
453 pub fn add_linear(&mut self, var: usize, value: f64) -> IsingResult<()> {
455 if var >= self.num_variables {
456 return Err(IsingError::InvalidQubit(var));
457 }
458
459 if !value.is_finite() {
460 return Err(IsingError::InvalidValue(format!(
461 "Linear term must be finite, got {value}"
462 )));
463 }
464
465 let current = *self.linear_terms.get(var).unwrap_or(&0.0);
466 self.linear_terms.set(var, current + value);
467 Ok(())
468 }
469
470 pub fn get_linear(&self, var: usize) -> IsingResult<f64> {
472 if var >= self.num_variables {
473 return Err(IsingError::InvalidQubit(var));
474 }
475
476 Ok(*self.linear_terms.get(var).unwrap_or(&0.0))
477 }
478
479 pub fn set_quadratic(&mut self, var1: usize, var2: usize, value: f64) -> IsingResult<()> {
481 if var1 >= self.num_variables || var2 >= self.num_variables {
482 return Err(IsingError::InvalidQubit(std::cmp::max(var1, var2)));
483 }
484
485 if var1 == var2 {
486 return Err(IsingError::InvalidCoupling(var1, var2));
487 }
488
489 if !value.is_finite() {
490 return Err(IsingError::InvalidValue(format!(
491 "Quadratic term must be finite, got {value}"
492 )));
493 }
494
495 let (var1, var2) = if var1 < var2 {
497 (var1, var2)
498 } else {
499 (var2, var1)
500 };
501 self.quadratic_terms.set(var1, var2, value);
502 Ok(())
503 }
504
505 pub fn get_quadratic(&self, var1: usize, var2: usize) -> IsingResult<f64> {
507 if var1 >= self.num_variables || var2 >= self.num_variables {
508 return Err(IsingError::InvalidQubit(std::cmp::max(var1, var2)));
509 }
510
511 if var1 == var2 {
512 return Err(IsingError::InvalidCoupling(var1, var2));
513 }
514
515 let (var1, var2) = if var1 < var2 {
517 (var1, var2)
518 } else {
519 (var2, var1)
520 };
521 Ok(*self.quadratic_terms.get(var1, var2).unwrap_or(&0.0))
522 }
523
524 #[must_use]
526 pub fn linear_terms(&self) -> Vec<(usize, f64)> {
527 self.linear_terms
528 .iter()
529 .map(|(var, value)| (var, *value))
530 .collect()
531 }
532
533 #[must_use]
535 pub fn quadratic_terms(&self) -> Vec<(usize, usize, f64)> {
536 self.quadratic_terms
537 .iter()
538 .map(|(var1, var2, value)| (var1, var2, *value))
539 .collect()
540 }
541
542 #[must_use]
544 pub fn to_dense_matrix(&self) -> scirs2_core::ndarray::Array2<f64> {
545 let mut matrix =
546 scirs2_core::ndarray::Array2::zeros((self.num_variables, self.num_variables));
547
548 for (var, value) in self.linear_terms.iter() {
550 matrix[[var, var]] = *value;
551 }
552
553 for (var1, var2, value) in self.quadratic_terms.iter() {
555 matrix[[var1, var2]] = *value;
556 matrix[[var2, var1]] = *value; }
558
559 matrix
560 }
561
562 #[must_use]
564 pub fn variable_map(&self) -> std::collections::HashMap<String, usize> {
565 (0..self.num_variables)
566 .map(|i| (format!("x{i}"), i))
567 .collect()
568 }
569
570 pub fn objective(&self, binary_vars: &[bool]) -> IsingResult<f64> {
577 if binary_vars.len() != self.num_variables {
578 return Err(IsingError::InvalidValue(format!(
579 "Binary configuration must have {} elements, got {}",
580 self.num_variables,
581 binary_vars.len()
582 )));
583 }
584
585 let linear_value: f64 = self
587 .linear_terms
588 .iter()
589 .map(|(var, value)| if binary_vars[var] { *value } else { 0.0 })
590 .sum();
591
592 let quadratic_value: f64 = self
594 .quadratic_terms
595 .iter()
596 .map(|(var1, var2, value)| {
597 if binary_vars[var1] && binary_vars[var2] {
598 *value
599 } else {
600 0.0
601 }
602 })
603 .sum();
604
605 Ok(linear_value + quadratic_value + self.offset)
606 }
607
608 #[must_use]
615 pub fn to_ising(&self) -> (IsingModel, f64) {
616 let mut ising = IsingModel::new(self.num_variables);
618
619 let mut offset_change = 0.0;
621
622 for (i, j, quadratic) in self.quadratic_terms.iter() {
624 let coupling = *quadratic / 4.0;
626 offset_change += coupling;
627 let _ = ising.set_coupling(i, j, coupling);
629 }
630
631 for i in 0..self.num_variables {
633 let linear = self.get_linear(i).unwrap_or(0.0);
635
636 let mut quadratic_sum = 0.0;
638 for j in 0..self.num_variables {
639 if i != j {
640 quadratic_sum += self.get_quadratic(i, j).unwrap_or(0.0);
641 }
642 }
643
644 let bias = (linear - quadratic_sum) / 2.0;
646
647 if bias != 0.0 {
648 let _ = ising.set_bias(i, bias);
650 }
651
652 offset_change += bias;
654 }
655
656 let total_offset = self.offset + offset_change;
658
659 (ising, total_offset)
660 }
661
662 #[must_use]
664 pub fn to_qubo_model(&self) -> Self {
665 self.clone()
666 }
667}
668
669impl Default for QuboModel {
671 fn default() -> Self {
672 Self::new(0)
673 }
674}
675
676#[cfg(test)]
677mod tests {
678 use super::*;
679
680 #[test]
681 fn test_ising_model_basic() {
682 let mut model = IsingModel::new(3);
684
685 assert!(model.set_bias(0, 1.0).is_ok());
687 assert!(model.set_bias(1, -0.5).is_ok());
688 assert!(model.set_bias(2, 0.0).is_ok());
689
690 assert!(model.set_coupling(0, 1, -1.0).is_ok());
692 assert!(model.set_coupling(1, 2, 0.5).is_ok());
693
694 assert_eq!(
696 model
697 .get_bias(0)
698 .expect("Failed to get bias for qubit 0 in test"),
699 1.0
700 );
701 assert_eq!(
702 model
703 .get_bias(1)
704 .expect("Failed to get bias for qubit 1 in test"),
705 -0.5
706 );
707 assert_eq!(
708 model
709 .get_bias(2)
710 .expect("Failed to get bias for qubit 2 in test"),
711 0.0
712 );
713
714 assert_eq!(
716 model
717 .get_coupling(0, 1)
718 .expect("Failed to get coupling(0,1) in test"),
719 -1.0
720 );
721 assert_eq!(
722 model
723 .get_coupling(1, 0)
724 .expect("Failed to get coupling(1,0) in test"),
725 -1.0
726 ); assert_eq!(
728 model
729 .get_coupling(1, 2)
730 .expect("Failed to get coupling(1,2) in test"),
731 0.5
732 );
733 assert_eq!(
734 model
735 .get_coupling(2, 1)
736 .expect("Failed to get coupling(2,1) in test"),
737 0.5
738 ); assert_eq!(
740 model
741 .get_coupling(0, 2)
742 .expect("Failed to get coupling(0,2) in test"),
743 0.0
744 ); }
746
747 #[test]
748 fn test_ising_model_energy() {
749 let mut model = IsingModel::new(3);
751
752 model
754 .set_bias(0, 1.0)
755 .expect("Failed to set bias for qubit 0 in energy test");
756 model
757 .set_bias(1, -0.5)
758 .expect("Failed to set bias for qubit 1 in energy test");
759
760 model
762 .set_coupling(0, 1, -1.0)
763 .expect("Failed to set coupling(0,1) in energy test");
764 model
765 .set_coupling(1, 2, 0.5)
766 .expect("Failed to set coupling(1,2) in energy test");
767
768 let energy = model
770 .energy(&[1, 1, 1])
771 .expect("Failed to calculate energy for [+1,+1,+1]");
772 assert_eq!(energy, 1.0 - 0.5 + (-1.0) * 1.0 * 1.0 + 0.5 * 1.0 * 1.0);
773
774 let energy = model
776 .energy(&[1, -1, 1])
777 .expect("Failed to calculate energy for [+1,-1,+1]");
778 assert_eq!(
779 energy,
780 1.0 * 1.0 + (-0.5) * (-1.0) + (-1.0) * 1.0 * (-1.0) + 0.5 * (-1.0) * 1.0
781 );
782 }
783
784 #[test]
785 fn test_qubo_model_basic() {
786 let mut model = QuboModel::new(3);
788
789 assert!(model.set_linear(0, 2.0).is_ok());
791 assert!(model.set_linear(1, -1.0).is_ok());
792 assert!(model.set_linear(2, 0.0).is_ok());
793
794 assert!(model.set_quadratic(0, 1, -4.0).is_ok());
796 assert!(model.set_quadratic(1, 2, 2.0).is_ok());
797 model.offset = 1.5;
798
799 assert_eq!(
801 model
802 .get_linear(0)
803 .expect("Failed to get linear term for variable 0 in test"),
804 2.0
805 );
806 assert_eq!(
807 model
808 .get_linear(1)
809 .expect("Failed to get linear term for variable 1 in test"),
810 -1.0
811 );
812 assert_eq!(
813 model
814 .get_linear(2)
815 .expect("Failed to get linear term for variable 2 in test"),
816 0.0
817 );
818
819 assert_eq!(
821 model
822 .get_quadratic(0, 1)
823 .expect("Failed to get quadratic term(0,1) in test"),
824 -4.0
825 );
826 assert_eq!(
827 model
828 .get_quadratic(1, 0)
829 .expect("Failed to get quadratic term(1,0) in test"),
830 -4.0
831 ); assert_eq!(
833 model
834 .get_quadratic(1, 2)
835 .expect("Failed to get quadratic term(1,2) in test"),
836 2.0
837 );
838 assert_eq!(
839 model
840 .get_quadratic(2, 1)
841 .expect("Failed to get quadratic term(2,1) in test"),
842 2.0
843 ); assert_eq!(
845 model
846 .get_quadratic(0, 2)
847 .expect("Failed to get quadratic term(0,2) in test"),
848 0.0
849 ); }
851
852 #[test]
853 fn test_qubo_model_objective() {
854 let mut model = QuboModel::new(3);
856
857 model
859 .set_linear(0, 2.0)
860 .expect("Failed to set linear term for variable 0 in objective test");
861 model
862 .set_linear(1, -1.0)
863 .expect("Failed to set linear term for variable 1 in objective test");
864
865 model
867 .set_quadratic(0, 1, -4.0)
868 .expect("Failed to set quadratic term(0,1) in objective test");
869 model
870 .set_quadratic(1, 2, 2.0)
871 .expect("Failed to set quadratic term(1,2) in objective test");
872 model.offset = 1.5;
873
874 let obj = model
876 .objective(&[true, true, true])
877 .expect("Failed to calculate objective for [true,true,true]");
878 assert_eq!(obj, 2.0 + (-1.0) + (-4.0) + 2.0 + 1.5);
879
880 let obj = model
882 .objective(&[true, false, true])
883 .expect("Failed to calculate objective for [true,false,true]");
884 assert_eq!(obj, 2.0 + 0.0 + 0.0 + 0.0 + 1.5);
885 }
886
887 #[test]
888 fn test_ising_to_qubo_conversion() {
889 let mut ising = IsingModel::new(3);
891
892 ising
894 .set_bias(0, 1.0)
895 .expect("Failed to set bias for qubit 0 in Ising-to-QUBO conversion test");
896 ising
897 .set_bias(1, -0.5)
898 .expect("Failed to set bias for qubit 1 in Ising-to-QUBO conversion test");
899
900 ising
902 .set_coupling(0, 1, -1.0)
903 .expect("Failed to set coupling(0,1) in Ising-to-QUBO conversion test");
904 ising
905 .set_coupling(1, 2, 0.5)
906 .expect("Failed to set coupling(1,2) in Ising-to-QUBO conversion test");
907
908 let qubo = ising.to_qubo();
910
911 println!(
913 "Ising model: biases = {:?}, couplings = {:?}",
914 ising.biases(),
915 ising.couplings()
916 );
917 println!(
918 "Resulting QUBO model: linear terms = {:?}, quadratic terms = {:?}, offset = {}",
919 qubo.linear_terms(),
920 qubo.quadratic_terms(),
921 qubo.offset
922 );
923
924 assert_eq!(
926 qubo.get_linear(0)
927 .expect("Failed to get QUBO linear term for variable 0"),
928 4.0
929 ); assert_eq!(
931 qubo.get_linear(1)
932 .expect("Failed to get QUBO linear term for variable 1"),
933 0.0
934 ); assert_eq!(
936 qubo.get_linear(2)
937 .expect("Failed to get QUBO linear term for variable 2"),
938 -1.0
939 ); assert_eq!(
943 qubo.get_quadratic(0, 1)
944 .expect("Failed to get QUBO quadratic term(0,1)"),
945 -4.0
946 );
947 assert_eq!(
948 qubo.get_quadratic(1, 2)
949 .expect("Failed to get QUBO quadratic term(1,2)"),
950 2.0
951 );
952 assert_eq!(qubo.offset, 0.5); }
954
955 #[test]
956 fn test_qubo_to_ising_conversion() {
957 let mut qubo = QuboModel::new(3);
959
960 qubo.set_linear(0, 2.0).expect(
962 "Failed to set QUBO linear term for variable 0 in QUBO-to-Ising conversion test",
963 );
964 qubo.set_linear(1, -1.0).expect(
965 "Failed to set QUBO linear term for variable 1 in QUBO-to-Ising conversion test",
966 );
967
968 qubo.set_quadratic(0, 1, -4.0)
970 .expect("Failed to set QUBO quadratic term(0,1) in QUBO-to-Ising conversion test");
971 qubo.set_quadratic(1, 2, 2.0)
972 .expect("Failed to set QUBO quadratic term(1,2) in QUBO-to-Ising conversion test");
973 qubo.offset = 1.5;
974
975 let (ising, offset) = qubo.to_ising();
977
978 println!(
980 "QUBO model: linear terms = {:?}, quadratic terms = {:?}, offset = {}",
981 qubo.linear_terms(),
982 qubo.quadratic_terms(),
983 qubo.offset
984 );
985 println!(
986 "Ising model: biases = {:?}, couplings = {:?}, offset = {}",
987 ising.biases(),
988 ising.couplings(),
989 offset
990 );
991
992 assert_eq!(
994 ising
995 .get_bias(0)
996 .expect("Failed to get Ising bias for qubit 0"),
997 3.0
998 );
999 assert_eq!(
1000 ising
1001 .get_bias(1)
1002 .expect("Failed to get Ising bias for qubit 1"),
1003 0.5
1004 ); assert_eq!(
1006 ising
1007 .get_bias(2)
1008 .expect("Failed to get Ising bias for qubit 2"),
1009 -1.0
1010 ); assert_eq!(
1014 ising
1015 .get_coupling(0, 1)
1016 .expect("Failed to get Ising coupling(0,1)"),
1017 -1.0
1018 );
1019 assert_eq!(
1020 ising
1021 .get_coupling(1, 2)
1022 .expect("Failed to get Ising coupling(1,2)"),
1023 0.5
1024 );
1025
1026 let qubo2 = ising.to_qubo();
1028 println!(
1029 "Back to QUBO model: linear terms = {:?}, quadratic terms = {:?}, offset = {}",
1030 qubo2.linear_terms(),
1031 qubo2.quadratic_terms(),
1032 qubo2.offset
1033 );
1034
1035 assert_eq!(
1037 qubo2
1038 .get_linear(0)
1039 .expect("Failed to get converted QUBO linear term for variable 0"),
1040 8.0
1041 ); assert_eq!(
1043 qubo2
1044 .get_linear(1)
1045 .expect("Failed to get converted QUBO linear term for variable 1"),
1046 2.0
1047 ); assert_eq!(
1049 qubo2
1050 .get_linear(2)
1051 .expect("Failed to get converted QUBO linear term for variable 2"),
1052 -3.0
1053 ); assert_eq!(
1055 qubo2
1056 .get_quadratic(0, 1)
1057 .expect("Failed to get converted QUBO quadratic term(0,1)"),
1058 -4.0
1059 );
1060 assert_eq!(
1061 qubo2
1062 .get_quadratic(1, 2)
1063 .expect("Failed to get converted QUBO quadratic term(1,2)"),
1064 2.0
1065 );
1066 }
1067}