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)]
99#[non_exhaustive]
100pub enum IsingError {
101 #[error("Invalid qubit index: {0}")]
103 InvalidQubit(usize),
104
105 #[error("Invalid coupling between qubits {0} and {1}")]
107 InvalidCoupling(usize, usize),
108
109 #[error("Invalid value: {0}")]
111 InvalidValue(String),
112
113 #[error("Model exceeds hardware constraints: {0}")]
115 HardwareConstraint(String),
116}
117
118pub type IsingResult<T> = Result<T, IsingError>;
120
121#[derive(Debug, Clone, Copy, PartialEq)]
123pub struct Coupling {
124 pub i: usize,
126 pub j: usize,
128 pub strength: f64,
130}
131
132impl Coupling {
133 pub fn new(i: usize, j: usize, strength: f64) -> IsingResult<Self> {
135 if i == j {
136 return Err(IsingError::InvalidCoupling(i, j));
137 }
138
139 if !strength.is_finite() {
140 return Err(IsingError::InvalidValue(format!(
141 "Coupling strength must be finite, got {strength}"
142 )));
143 }
144
145 if i < j {
147 Ok(Self { i, j, strength })
148 } else {
149 Ok(Self {
150 i: j,
151 j: i,
152 strength,
153 })
154 }
155 }
156
157 #[must_use]
159 pub const fn involves(&self, qubit: usize) -> bool {
160 self.i == qubit || self.j == qubit
161 }
162}
163
164#[derive(Debug, Clone)]
185pub struct IsingModel {
186 pub num_qubits: usize,
188
189 biases: SparseVector<f64>,
191
192 couplings: CooMatrix<f64>,
194}
195
196impl IsingModel {
197 #[must_use]
199 pub fn new(num_qubits: usize) -> Self {
200 Self {
201 num_qubits,
202 biases: SparseVector::new(num_qubits),
203 couplings: CooMatrix::new(num_qubits, num_qubits),
204 }
205 }
206
207 pub fn set_bias(&mut self, qubit: usize, bias: f64) -> IsingResult<()> {
209 if qubit >= self.num_qubits {
210 return Err(IsingError::InvalidQubit(qubit));
211 }
212
213 if !bias.is_finite() {
214 return Err(IsingError::InvalidValue(format!(
215 "Bias must be finite, got {bias}"
216 )));
217 }
218
219 self.biases.set(qubit, bias);
220 Ok(())
221 }
222
223 pub fn get_bias(&self, qubit: usize) -> IsingResult<f64> {
225 if qubit >= self.num_qubits {
226 return Err(IsingError::InvalidQubit(qubit));
227 }
228
229 Ok(*self.biases.get(qubit).unwrap_or(&0.0))
230 }
231
232 pub fn set_coupling(&mut self, i: usize, j: usize, strength: f64) -> IsingResult<()> {
234 if i >= self.num_qubits || j >= self.num_qubits {
235 return Err(IsingError::InvalidQubit(std::cmp::max(i, j)));
236 }
237
238 if i == j {
239 return Err(IsingError::InvalidCoupling(i, j));
240 }
241
242 if !strength.is_finite() {
243 return Err(IsingError::InvalidValue(format!(
244 "Coupling strength must be finite, got {strength}"
245 )));
246 }
247
248 let (i, j) = if i < j { (i, j) } else { (j, i) };
250 self.couplings.set(i, j, strength);
251 Ok(())
252 }
253
254 pub fn get_coupling(&self, i: usize, j: usize) -> IsingResult<f64> {
256 if i >= self.num_qubits || j >= self.num_qubits {
257 return Err(IsingError::InvalidQubit(std::cmp::max(i, j)));
258 }
259
260 if i == j {
261 return Err(IsingError::InvalidCoupling(i, j));
262 }
263
264 let (i, j) = if i < j { (i, j) } else { (j, i) };
266 Ok(*self.couplings.get(i, j).unwrap_or(&0.0))
267 }
268
269 #[must_use]
271 pub fn biases(&self) -> Vec<(usize, f64)> {
272 self.biases
273 .iter()
274 .map(|(qubit, bias)| (qubit, *bias))
275 .collect()
276 }
277
278 #[must_use]
280 pub fn couplings(&self) -> Vec<Coupling> {
281 self.couplings
282 .iter()
283 .map(|(i, j, strength)| Coupling {
284 i,
285 j,
286 strength: *strength,
287 })
288 .collect()
289 }
290
291 pub fn energy(&self, spins: &[i8]) -> IsingResult<f64> {
298 if spins.len() != self.num_qubits {
299 return Err(IsingError::InvalidValue(format!(
300 "Spin configuration must have {} elements, got {}",
301 self.num_qubits,
302 spins.len()
303 )));
304 }
305
306 for (i, &spin) in spins.iter().enumerate() {
308 if spin != -1 && spin != 1 {
309 return Err(IsingError::InvalidValue(format!(
310 "Spin values must be -1 or 1, got {spin} at index {i}"
311 )));
312 }
313 }
314
315 let bias_energy: f64 = self
317 .biases
318 .iter()
319 .map(|(qubit, bias)| *bias * f64::from(spins[qubit]))
320 .sum();
321
322 let coupling_energy: f64 = self
324 .couplings
325 .iter()
326 .map(|(i, j, strength)| *strength * f64::from(spins[i]) * f64::from(spins[j]))
327 .sum();
328
329 Ok(bias_energy + coupling_energy)
330 }
331
332 #[must_use]
339 pub fn to_qubo(&self) -> QuboModel {
340 let mut qubo = QuboModel::new(self.num_qubits);
342
343 let mut linear_terms = HashMap::new();
345
346 for (i, j, coupling) in self.couplings.iter() {
348 let quadratic_term = 4.0 * *coupling;
350 let _ = qubo.set_quadratic(i, j, quadratic_term);
351
352 *linear_terms.entry(i).or_insert(0.0) -= 2.0 * *coupling;
354 *linear_terms.entry(j).or_insert(0.0) -= 2.0 * *coupling;
355 }
356
357 for (i, bias) in self.biases.iter() {
359 let linear_adj = *linear_terms.get(&i).unwrap_or(&0.0);
360 let linear_term = 2.0f64.mul_add(*bias, linear_adj);
361 let _ = qubo.set_linear(i, linear_term);
362 }
363
364 for (i, adj) in linear_terms {
366 let has_bias = self.biases.get(i).unwrap_or(&0.0).abs() > 1e-10;
367 if !has_bias {
368 let _ = qubo.set_linear(i, adj);
369 }
370 }
371
372 let coupling_sum: f64 = self
374 .couplings
375 .iter()
376 .map(|(_, _, strength)| *strength)
377 .sum();
378 qubo.offset = -coupling_sum;
379
380 qubo
381 }
382
383 #[must_use]
385 pub fn from_qubo(qubo: &QuboModel) -> Self {
386 let mut ising = Self::new(qubo.num_variables);
388
389 for (i, linear) in qubo.linear_terms.iter() {
391 let bias = *linear / 2.0;
393 let _ = ising.set_bias(i, bias);
395 }
396
397 for (i, j, quadratic) in qubo.quadratic_terms.iter() {
399 let coupling = *quadratic / 4.0;
401 let _ = ising.set_coupling(i, j, coupling);
403 }
404
405 ising
406 }
407}
408
409impl Default for IsingModel {
411 fn default() -> Self {
412 Self::new(0)
413 }
414}
415
416#[derive(Debug, Clone)]
423pub struct QuboModel {
424 pub num_variables: usize,
426
427 linear_terms: SparseVector<f64>,
429
430 quadratic_terms: CooMatrix<f64>,
432
433 pub offset: f64,
435}
436
437impl QuboModel {
438 #[must_use]
440 pub fn new(num_variables: usize) -> Self {
441 Self {
442 num_variables,
443 linear_terms: SparseVector::new(num_variables),
444 quadratic_terms: CooMatrix::new(num_variables, num_variables),
445 offset: 0.0,
446 }
447 }
448
449 pub fn set_linear(&mut self, var: usize, value: f64) -> IsingResult<()> {
451 if var >= self.num_variables {
452 return Err(IsingError::InvalidQubit(var));
453 }
454
455 if !value.is_finite() {
456 return Err(IsingError::InvalidValue(format!(
457 "Linear term must be finite, got {value}"
458 )));
459 }
460
461 self.linear_terms.set(var, value);
462 Ok(())
463 }
464
465 pub fn add_linear(&mut self, var: usize, value: f64) -> IsingResult<()> {
467 if var >= self.num_variables {
468 return Err(IsingError::InvalidQubit(var));
469 }
470
471 if !value.is_finite() {
472 return Err(IsingError::InvalidValue(format!(
473 "Linear term must be finite, got {value}"
474 )));
475 }
476
477 let current = *self.linear_terms.get(var).unwrap_or(&0.0);
478 self.linear_terms.set(var, current + value);
479 Ok(())
480 }
481
482 pub fn get_linear(&self, var: usize) -> IsingResult<f64> {
484 if var >= self.num_variables {
485 return Err(IsingError::InvalidQubit(var));
486 }
487
488 Ok(*self.linear_terms.get(var).unwrap_or(&0.0))
489 }
490
491 pub fn set_quadratic(&mut self, var1: usize, var2: usize, value: f64) -> IsingResult<()> {
493 if var1 >= self.num_variables || var2 >= self.num_variables {
494 return Err(IsingError::InvalidQubit(std::cmp::max(var1, var2)));
495 }
496
497 if var1 == var2 {
498 return Err(IsingError::InvalidCoupling(var1, var2));
499 }
500
501 if !value.is_finite() {
502 return Err(IsingError::InvalidValue(format!(
503 "Quadratic term must be finite, got {value}"
504 )));
505 }
506
507 let (var1, var2) = if var1 < var2 {
509 (var1, var2)
510 } else {
511 (var2, var1)
512 };
513 self.quadratic_terms.set(var1, var2, value);
514 Ok(())
515 }
516
517 pub fn get_quadratic(&self, var1: usize, var2: usize) -> IsingResult<f64> {
519 if var1 >= self.num_variables || var2 >= self.num_variables {
520 return Err(IsingError::InvalidQubit(std::cmp::max(var1, var2)));
521 }
522
523 if var1 == var2 {
524 return Err(IsingError::InvalidCoupling(var1, var2));
525 }
526
527 let (var1, var2) = if var1 < var2 {
529 (var1, var2)
530 } else {
531 (var2, var1)
532 };
533 Ok(*self.quadratic_terms.get(var1, var2).unwrap_or(&0.0))
534 }
535
536 #[must_use]
538 pub fn linear_terms(&self) -> Vec<(usize, f64)> {
539 self.linear_terms
540 .iter()
541 .map(|(var, value)| (var, *value))
542 .collect()
543 }
544
545 #[must_use]
547 pub fn quadratic_terms(&self) -> Vec<(usize, usize, f64)> {
548 self.quadratic_terms
549 .iter()
550 .map(|(var1, var2, value)| (var1, var2, *value))
551 .collect()
552 }
553
554 #[must_use]
556 pub fn to_dense_matrix(&self) -> scirs2_core::ndarray::Array2<f64> {
557 let mut matrix =
558 scirs2_core::ndarray::Array2::zeros((self.num_variables, self.num_variables));
559
560 for (var, value) in self.linear_terms.iter() {
562 matrix[[var, var]] = *value;
563 }
564
565 for (var1, var2, value) in self.quadratic_terms.iter() {
567 matrix[[var1, var2]] = *value;
568 matrix[[var2, var1]] = *value; }
570
571 matrix
572 }
573
574 #[must_use]
576 pub fn variable_map(&self) -> std::collections::HashMap<String, usize> {
577 (0..self.num_variables)
578 .map(|i| (format!("x{i}"), i))
579 .collect()
580 }
581
582 pub fn objective(&self, binary_vars: &[bool]) -> IsingResult<f64> {
589 if binary_vars.len() != self.num_variables {
590 return Err(IsingError::InvalidValue(format!(
591 "Binary configuration must have {} elements, got {}",
592 self.num_variables,
593 binary_vars.len()
594 )));
595 }
596
597 let linear_value: f64 = self
599 .linear_terms
600 .iter()
601 .map(|(var, value)| if binary_vars[var] { *value } else { 0.0 })
602 .sum();
603
604 let quadratic_value: f64 = self
606 .quadratic_terms
607 .iter()
608 .map(|(var1, var2, value)| {
609 if binary_vars[var1] && binary_vars[var2] {
610 *value
611 } else {
612 0.0
613 }
614 })
615 .sum();
616
617 Ok(linear_value + quadratic_value + self.offset)
618 }
619
620 #[must_use]
627 pub fn to_ising(&self) -> (IsingModel, f64) {
628 let mut ising = IsingModel::new(self.num_variables);
630
631 let mut offset_change = 0.0;
633
634 for (i, j, quadratic) in self.quadratic_terms.iter() {
636 let coupling = *quadratic / 4.0;
638 offset_change += coupling;
639 let _ = ising.set_coupling(i, j, coupling);
641 }
642
643 for i in 0..self.num_variables {
645 let linear = self.get_linear(i).unwrap_or(0.0);
647
648 let mut quadratic_sum = 0.0;
650 for j in 0..self.num_variables {
651 if i != j {
652 quadratic_sum += self.get_quadratic(i, j).unwrap_or(0.0);
653 }
654 }
655
656 let bias = (linear - quadratic_sum) / 2.0;
658
659 if bias != 0.0 {
660 let _ = ising.set_bias(i, bias);
662 }
663
664 offset_change += bias;
666 }
667
668 let total_offset = self.offset + offset_change;
670
671 (ising, total_offset)
672 }
673
674 #[must_use]
676 pub fn to_qubo_model(&self) -> Self {
677 self.clone()
678 }
679}
680
681impl Default for QuboModel {
683 fn default() -> Self {
684 Self::new(0)
685 }
686}
687
688#[cfg(test)]
689mod tests {
690 use super::*;
691
692 #[test]
693 fn test_ising_model_basic() {
694 let mut model = IsingModel::new(3);
696
697 assert!(model.set_bias(0, 1.0).is_ok());
699 assert!(model.set_bias(1, -0.5).is_ok());
700 assert!(model.set_bias(2, 0.0).is_ok());
701
702 assert!(model.set_coupling(0, 1, -1.0).is_ok());
704 assert!(model.set_coupling(1, 2, 0.5).is_ok());
705
706 assert_eq!(
708 model
709 .get_bias(0)
710 .expect("Failed to get bias for qubit 0 in test"),
711 1.0
712 );
713 assert_eq!(
714 model
715 .get_bias(1)
716 .expect("Failed to get bias for qubit 1 in test"),
717 -0.5
718 );
719 assert_eq!(
720 model
721 .get_bias(2)
722 .expect("Failed to get bias for qubit 2 in test"),
723 0.0
724 );
725
726 assert_eq!(
728 model
729 .get_coupling(0, 1)
730 .expect("Failed to get coupling(0,1) in test"),
731 -1.0
732 );
733 assert_eq!(
734 model
735 .get_coupling(1, 0)
736 .expect("Failed to get coupling(1,0) in test"),
737 -1.0
738 ); assert_eq!(
740 model
741 .get_coupling(1, 2)
742 .expect("Failed to get coupling(1,2) in test"),
743 0.5
744 );
745 assert_eq!(
746 model
747 .get_coupling(2, 1)
748 .expect("Failed to get coupling(2,1) in test"),
749 0.5
750 ); assert_eq!(
752 model
753 .get_coupling(0, 2)
754 .expect("Failed to get coupling(0,2) in test"),
755 0.0
756 ); }
758
759 #[test]
760 fn test_ising_model_energy() {
761 let mut model = IsingModel::new(3);
763
764 model
766 .set_bias(0, 1.0)
767 .expect("Failed to set bias for qubit 0 in energy test");
768 model
769 .set_bias(1, -0.5)
770 .expect("Failed to set bias for qubit 1 in energy test");
771
772 model
774 .set_coupling(0, 1, -1.0)
775 .expect("Failed to set coupling(0,1) in energy test");
776 model
777 .set_coupling(1, 2, 0.5)
778 .expect("Failed to set coupling(1,2) in energy test");
779
780 let energy = model
782 .energy(&[1, 1, 1])
783 .expect("Failed to calculate energy for [+1,+1,+1]");
784 assert_eq!(energy, 1.0 - 0.5 + (-1.0) * 1.0 * 1.0 + 0.5 * 1.0 * 1.0);
785
786 let energy = model
788 .energy(&[1, -1, 1])
789 .expect("Failed to calculate energy for [+1,-1,+1]");
790 assert_eq!(
791 energy,
792 1.0 * 1.0 + (-0.5) * (-1.0) + (-1.0) * 1.0 * (-1.0) + 0.5 * (-1.0) * 1.0
793 );
794 }
795
796 #[test]
797 fn test_qubo_model_basic() {
798 let mut model = QuboModel::new(3);
800
801 assert!(model.set_linear(0, 2.0).is_ok());
803 assert!(model.set_linear(1, -1.0).is_ok());
804 assert!(model.set_linear(2, 0.0).is_ok());
805
806 assert!(model.set_quadratic(0, 1, -4.0).is_ok());
808 assert!(model.set_quadratic(1, 2, 2.0).is_ok());
809 model.offset = 1.5;
810
811 assert_eq!(
813 model
814 .get_linear(0)
815 .expect("Failed to get linear term for variable 0 in test"),
816 2.0
817 );
818 assert_eq!(
819 model
820 .get_linear(1)
821 .expect("Failed to get linear term for variable 1 in test"),
822 -1.0
823 );
824 assert_eq!(
825 model
826 .get_linear(2)
827 .expect("Failed to get linear term for variable 2 in test"),
828 0.0
829 );
830
831 assert_eq!(
833 model
834 .get_quadratic(0, 1)
835 .expect("Failed to get quadratic term(0,1) in test"),
836 -4.0
837 );
838 assert_eq!(
839 model
840 .get_quadratic(1, 0)
841 .expect("Failed to get quadratic term(1,0) in test"),
842 -4.0
843 ); assert_eq!(
845 model
846 .get_quadratic(1, 2)
847 .expect("Failed to get quadratic term(1,2) in test"),
848 2.0
849 );
850 assert_eq!(
851 model
852 .get_quadratic(2, 1)
853 .expect("Failed to get quadratic term(2,1) in test"),
854 2.0
855 ); assert_eq!(
857 model
858 .get_quadratic(0, 2)
859 .expect("Failed to get quadratic term(0,2) in test"),
860 0.0
861 ); }
863
864 #[test]
865 fn test_qubo_model_objective() {
866 let mut model = QuboModel::new(3);
868
869 model
871 .set_linear(0, 2.0)
872 .expect("Failed to set linear term for variable 0 in objective test");
873 model
874 .set_linear(1, -1.0)
875 .expect("Failed to set linear term for variable 1 in objective test");
876
877 model
879 .set_quadratic(0, 1, -4.0)
880 .expect("Failed to set quadratic term(0,1) in objective test");
881 model
882 .set_quadratic(1, 2, 2.0)
883 .expect("Failed to set quadratic term(1,2) in objective test");
884 model.offset = 1.5;
885
886 let obj = model
888 .objective(&[true, true, true])
889 .expect("Failed to calculate objective for [true,true,true]");
890 assert_eq!(obj, 2.0 + (-1.0) + (-4.0) + 2.0 + 1.5);
891
892 let obj = model
894 .objective(&[true, false, true])
895 .expect("Failed to calculate objective for [true,false,true]");
896 assert_eq!(obj, 2.0 + 0.0 + 0.0 + 0.0 + 1.5);
897 }
898
899 #[test]
900 fn test_ising_to_qubo_conversion() {
901 let mut ising = IsingModel::new(3);
903
904 ising
906 .set_bias(0, 1.0)
907 .expect("Failed to set bias for qubit 0 in Ising-to-QUBO conversion test");
908 ising
909 .set_bias(1, -0.5)
910 .expect("Failed to set bias for qubit 1 in Ising-to-QUBO conversion test");
911
912 ising
914 .set_coupling(0, 1, -1.0)
915 .expect("Failed to set coupling(0,1) in Ising-to-QUBO conversion test");
916 ising
917 .set_coupling(1, 2, 0.5)
918 .expect("Failed to set coupling(1,2) in Ising-to-QUBO conversion test");
919
920 let qubo = ising.to_qubo();
922
923 println!(
925 "Ising model: biases = {:?}, couplings = {:?}",
926 ising.biases(),
927 ising.couplings()
928 );
929 println!(
930 "Resulting QUBO model: linear terms = {:?}, quadratic terms = {:?}, offset = {}",
931 qubo.linear_terms(),
932 qubo.quadratic_terms(),
933 qubo.offset
934 );
935
936 assert_eq!(
938 qubo.get_linear(0)
939 .expect("Failed to get QUBO linear term for variable 0"),
940 4.0
941 ); assert_eq!(
943 qubo.get_linear(1)
944 .expect("Failed to get QUBO linear term for variable 1"),
945 0.0
946 ); assert_eq!(
948 qubo.get_linear(2)
949 .expect("Failed to get QUBO linear term for variable 2"),
950 -1.0
951 ); assert_eq!(
955 qubo.get_quadratic(0, 1)
956 .expect("Failed to get QUBO quadratic term(0,1)"),
957 -4.0
958 );
959 assert_eq!(
960 qubo.get_quadratic(1, 2)
961 .expect("Failed to get QUBO quadratic term(1,2)"),
962 2.0
963 );
964 assert_eq!(qubo.offset, 0.5); }
966
967 #[test]
968 fn test_qubo_to_ising_conversion() {
969 let mut qubo = QuboModel::new(3);
971
972 qubo.set_linear(0, 2.0).expect(
974 "Failed to set QUBO linear term for variable 0 in QUBO-to-Ising conversion test",
975 );
976 qubo.set_linear(1, -1.0).expect(
977 "Failed to set QUBO linear term for variable 1 in QUBO-to-Ising conversion test",
978 );
979
980 qubo.set_quadratic(0, 1, -4.0)
982 .expect("Failed to set QUBO quadratic term(0,1) in QUBO-to-Ising conversion test");
983 qubo.set_quadratic(1, 2, 2.0)
984 .expect("Failed to set QUBO quadratic term(1,2) in QUBO-to-Ising conversion test");
985 qubo.offset = 1.5;
986
987 let (ising, offset) = qubo.to_ising();
989
990 println!(
992 "QUBO model: linear terms = {:?}, quadratic terms = {:?}, offset = {}",
993 qubo.linear_terms(),
994 qubo.quadratic_terms(),
995 qubo.offset
996 );
997 println!(
998 "Ising model: biases = {:?}, couplings = {:?}, offset = {}",
999 ising.biases(),
1000 ising.couplings(),
1001 offset
1002 );
1003
1004 assert_eq!(
1006 ising
1007 .get_bias(0)
1008 .expect("Failed to get Ising bias for qubit 0"),
1009 3.0
1010 );
1011 assert_eq!(
1012 ising
1013 .get_bias(1)
1014 .expect("Failed to get Ising bias for qubit 1"),
1015 0.5
1016 ); assert_eq!(
1018 ising
1019 .get_bias(2)
1020 .expect("Failed to get Ising bias for qubit 2"),
1021 -1.0
1022 ); assert_eq!(
1026 ising
1027 .get_coupling(0, 1)
1028 .expect("Failed to get Ising coupling(0,1)"),
1029 -1.0
1030 );
1031 assert_eq!(
1032 ising
1033 .get_coupling(1, 2)
1034 .expect("Failed to get Ising coupling(1,2)"),
1035 0.5
1036 );
1037
1038 let qubo2 = ising.to_qubo();
1040 println!(
1041 "Back to QUBO model: linear terms = {:?}, quadratic terms = {:?}, offset = {}",
1042 qubo2.linear_terms(),
1043 qubo2.quadratic_terms(),
1044 qubo2.offset
1045 );
1046
1047 assert_eq!(
1049 qubo2
1050 .get_linear(0)
1051 .expect("Failed to get converted QUBO linear term for variable 0"),
1052 8.0
1053 ); assert_eq!(
1055 qubo2
1056 .get_linear(1)
1057 .expect("Failed to get converted QUBO linear term for variable 1"),
1058 2.0
1059 ); assert_eq!(
1061 qubo2
1062 .get_linear(2)
1063 .expect("Failed to get converted QUBO linear term for variable 2"),
1064 -3.0
1065 ); assert_eq!(
1067 qubo2
1068 .get_quadratic(0, 1)
1069 .expect("Failed to get converted QUBO quadratic term(0,1)"),
1070 -4.0
1071 );
1072 assert_eq!(
1073 qubo2
1074 .get_quadratic(1, 2)
1075 .expect("Failed to get converted QUBO quadratic term(1,2)"),
1076 2.0
1077 );
1078 }
1079}