1use crate::error::{QuantumError, QuantumResult};
53use crate::gates::controlled::apply_cnot;
54use crate::gates::parametric::gate_ry;
55use crate::statevec::apply_1q::apply_1q_inplace;
56use crate::statevec::state::StateVector;
57use num_complex::Complex;
58
59type Complex32 = Complex<f32>;
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum Pauli {
64 I,
66 X,
68 Y,
70 Z,
72}
73
74impl Pauli {
75 #[must_use]
77 fn matrix(self) -> [[Complex32; 2]; 2] {
78 let zero = Complex32::new(0.0, 0.0);
79 let one = Complex32::new(1.0, 0.0);
80 let neg = Complex32::new(-1.0, 0.0);
81 let i = Complex32::new(0.0, 1.0);
82 let ni = Complex32::new(0.0, -1.0);
83 match self {
84 Pauli::I => [[one, zero], [zero, one]],
85 Pauli::X => [[zero, one], [one, zero]],
86 Pauli::Y => [[zero, ni], [i, zero]],
87 Pauli::Z => [[one, zero], [zero, neg]],
88 }
89 }
90}
91
92#[derive(Debug, Clone)]
95pub struct PauliTerm {
96 pub coeff: f32,
98 pub paulis: Vec<Pauli>,
100}
101
102#[derive(Debug, Clone)]
104pub struct LcuOperator {
105 n_qubits: usize,
107 matrix: Vec<Complex32>,
109}
110
111impl LcuOperator {
112 pub fn new(terms: &[PauliTerm], n_qubits: usize) -> QuantumResult<Self> {
122 if terms.is_empty() {
123 return Err(QuantumError::EmptyInput);
124 }
125 if n_qubits == 0 || n_qubits > 8 {
126 return Err(QuantumError::InvalidQubitCount { n: n_qubits });
127 }
128 let dim = 1usize << n_qubits;
129 let mut matrix = vec![Complex32::new(0.0, 0.0); dim * dim];
130 for term in terms {
131 if term.paulis.len() != n_qubits {
132 return Err(QuantumError::DimensionMismatch {
133 expected: n_qubits,
134 got: term.paulis.len(),
135 });
136 }
137 let pmat = pauli_string_matrix(&term.paulis, n_qubits);
138 let c = Complex32::new(term.coeff, 0.0);
139 for (dst, src) in matrix.iter_mut().zip(pmat.iter()) {
140 *dst += c * *src;
141 }
142 }
143 Ok(Self { n_qubits, matrix })
144 }
145
146 #[must_use]
148 pub fn n_qubits(&self) -> usize {
149 self.n_qubits
150 }
151
152 #[must_use]
154 pub fn matrix(&self) -> &[Complex32] {
155 &self.matrix
156 }
157
158 #[must_use]
160 fn apply_to(&self, v: &[Complex32]) -> Vec<Complex32> {
161 let dim = 1usize << self.n_qubits;
162 let mut out = vec![Complex32::new(0.0, 0.0); dim];
163 for (r, slot) in out.iter_mut().enumerate() {
164 let mut acc = Complex32::new(0.0, 0.0);
165 for (col, &xv) in v.iter().enumerate() {
166 acc += self.matrix[r * dim + col] * xv;
167 }
168 *slot = acc;
169 }
170 out
171 }
172
173 #[must_use]
176 pub fn classical_solution(&self, b: &[Complex32]) -> Option<Vec<Complex32>> {
177 let dim = 1usize << self.n_qubits;
178 if b.len() != dim {
179 return None;
180 }
181 let mut aug = vec![Complex::<f64>::new(0.0, 0.0); dim * (dim + 1)];
183 for r in 0..dim {
184 for c in 0..dim {
185 let z = self.matrix[r * dim + c];
186 aug[r * (dim + 1) + c] = Complex::<f64>::new(z.re as f64, z.im as f64);
187 }
188 let bz = b[r];
189 aug[r * (dim + 1) + dim] = Complex::<f64>::new(bz.re as f64, bz.im as f64);
190 }
191 for col in 0..dim {
193 let mut pivot_row = col;
195 let mut best = aug[col * (dim + 1) + col].norm();
196 for r in (col + 1)..dim {
197 let mag = aug[r * (dim + 1) + col].norm();
198 if mag > best {
199 best = mag;
200 pivot_row = r;
201 }
202 }
203 if best < 1e-12 {
204 return None; }
206 if pivot_row != col {
207 for c in 0..=dim {
208 aug.swap(col * (dim + 1) + c, pivot_row * (dim + 1) + c);
209 }
210 }
211 let pivot = aug[col * (dim + 1) + col];
212 for c in col..=dim {
213 aug[col * (dim + 1) + c] /= pivot;
214 }
215 for r in 0..dim {
216 if r == col {
217 continue;
218 }
219 let factor = aug[r * (dim + 1) + col];
220 if factor.norm() < 1e-300 {
221 continue;
222 }
223 for c in col..=dim {
224 let sub = factor * aug[col * (dim + 1) + c];
225 aug[r * (dim + 1) + c] -= sub;
226 }
227 }
228 }
229 let mut x: Vec<Complex32> = (0..dim)
231 .map(|r| {
232 let z = aug[r * (dim + 1) + dim];
233 Complex32::new(z.re as f32, z.im as f32)
234 })
235 .collect();
236 let norm: f32 = x.iter().map(|z| z.norm_sqr()).sum::<f32>().sqrt();
237 if norm < 1e-9 {
238 return None;
239 }
240 for z in &mut x {
241 *z /= norm;
242 }
243 Some(x)
244 }
245}
246
247fn pauli_string_matrix(paulis: &[Pauli], n_qubits: usize) -> Vec<Complex32> {
250 let dim = 1usize << n_qubits;
251 let single: Vec<[[Complex32; 2]; 2]> = paulis.iter().map(|p| p.matrix()).collect();
252 let mut out = vec![Complex32::new(0.0, 0.0); dim * dim];
253 for row in 0..dim {
254 for col in 0..dim {
255 let mut elem = Complex32::new(1.0, 0.0);
257 for (q, pmat) in single.iter().enumerate() {
258 let rb = (row >> q) & 1;
259 let cb = (col >> q) & 1;
260 elem *= pmat[rb][cb];
261 if elem.re == 0.0 && elem.im == 0.0 {
262 break;
263 }
264 }
265 out[row * dim + col] = elem;
266 }
267 }
268 out
269}
270
271#[derive(Debug, Clone)]
274pub struct HardwareEfficientAnsatz {
275 n_qubits: usize,
277 n_layers: usize,
279}
280
281impl HardwareEfficientAnsatz {
282 pub fn new(n_qubits: usize, n_layers: usize) -> QuantumResult<Self> {
287 if n_qubits == 0 || n_layers == 0 {
288 return Err(QuantumError::IncompatibleAnsatz);
289 }
290 Ok(Self { n_qubits, n_layers })
291 }
292
293 #[must_use]
295 pub fn n_params(&self) -> usize {
296 self.n_qubits * self.n_layers
297 }
298
299 pub fn prepare(&self, params: &[f32]) -> QuantumResult<StateVector> {
305 if params.len() != self.n_params() {
306 return Err(QuantumError::DimensionMismatch {
307 expected: self.n_params(),
308 got: params.len(),
309 });
310 }
311 let mut sv = StateVector::new_zero_state(self.n_qubits)?;
312 let mut idx = 0usize;
313 for _layer in 0..self.n_layers {
314 for q in 0..self.n_qubits {
315 apply_1q_inplace(&mut sv, q, &gate_ry(params[idx]))?;
316 idx += 1;
317 }
318 for q in 0..self.n_qubits.saturating_sub(1) {
320 apply_cnot(&mut sv, q, q + 1)?;
321 }
322 }
323 Ok(sv)
324 }
325}
326
327#[derive(Debug, Clone)]
330pub struct VqlsSolver {
331 operator: LcuOperator,
332 b: Vec<Complex32>,
334 ansatz: HardwareEfficientAnsatz,
335}
336
337#[derive(Debug, Clone)]
339pub struct VqlsResult {
340 pub params: Vec<f32>,
342 pub final_cost: f64,
344 pub cost_history: Vec<f64>,
346 pub solution: Vec<Complex32>,
348}
349
350impl VqlsSolver {
351 pub fn new(
359 operator: LcuOperator,
360 b: &[Complex32],
361 ansatz: HardwareEfficientAnsatz,
362 ) -> QuantumResult<Self> {
363 let dim = 1usize << operator.n_qubits();
364 if b.len() != dim {
365 return Err(QuantumError::DimensionMismatch {
366 expected: dim,
367 got: b.len(),
368 });
369 }
370 if ansatz.n_qubits != operator.n_qubits() {
371 return Err(QuantumError::DimensionMismatch {
372 expected: operator.n_qubits(),
373 got: ansatz.n_qubits,
374 });
375 }
376 let norm: f32 = b.iter().map(|z| z.norm_sqr()).sum::<f32>().sqrt();
377 if norm < 1e-12 {
378 return Err(QuantumError::InvalidParameter {
379 name: "right-hand side b must be non-zero".into(),
380 });
381 }
382 let inv = 1.0 / norm;
383 let b_norm: Vec<Complex32> = b.iter().map(|z| *z * inv).collect();
384 Ok(Self {
385 operator,
386 b: b_norm,
387 ansatz,
388 })
389 }
390
391 pub fn cost(&self, params: &[f32]) -> QuantumResult<f64> {
397 let x = self.ansatz.prepare(params)?;
398 Ok(self.cost_of_state(&x.amps))
399 }
400
401 fn cost_of_state(&self, x: &[Complex32]) -> f64 {
404 let ax = self.operator.apply_to(x);
405 let denom: f64 = ax.iter().map(|z| z.norm_sqr() as f64).sum();
407 if denom < 1e-18 {
408 return 1.0;
410 }
411 let mut inner = Complex::<f64>::new(0.0, 0.0);
413 for (bz, az) in self.b.iter().zip(ax.iter()) {
414 let b64 = Complex::<f64>::new(bz.re as f64, bz.im as f64);
415 let a64 = Complex::<f64>::new(az.re as f64, az.im as f64);
416 inner += b64.conj() * a64;
417 }
418 let overlap = inner.norm_sqr();
419 let cost = 1.0 - overlap / denom;
420 cost.max(0.0)
422 }
423
424 pub fn solution_state(&self, params: &[f32]) -> QuantumResult<Vec<Complex32>> {
429 let mut x = self.ansatz.prepare(params)?;
430 x.normalize_inplace();
431 Ok(x.amps)
432 }
433
434 pub fn optimize(
445 &self,
446 init_params: &[f32],
447 max_iters: usize,
448 lr: f32,
449 eps: f32,
450 tol: f64,
451 ) -> QuantumResult<VqlsResult> {
452 let n = self.ansatz.n_params();
453 if init_params.len() != n {
454 return Err(QuantumError::DimensionMismatch {
455 expected: n,
456 got: init_params.len(),
457 });
458 }
459 let mut params = init_params.to_vec();
460 let mut current = self.cost(¶ms)?;
461 let mut history = vec![current];
462 let mut step = lr;
463
464 for _ in 0..max_iters {
465 if current < tol {
466 break;
467 }
468 let mut grad = vec![0.0_f32; n];
470 for (i, g) in grad.iter_mut().enumerate() {
471 let saved = params[i];
472 params[i] = saved + eps;
473 let c_plus = self.cost(¶ms)?;
474 params[i] = saved - eps;
475 let c_minus = self.cost(¶ms)?;
476 params[i] = saved;
477 *g = ((c_plus - c_minus) / (2.0 * eps as f64)) as f32;
478 }
479 let mut accepted = false;
481 let mut local_step = step;
482 for _try in 0..20 {
483 let trial: Vec<f32> = params
484 .iter()
485 .zip(grad.iter())
486 .map(|(&p, &g)| p - local_step * g)
487 .collect();
488 let c_trial = self.cost(&trial)?;
489 if c_trial < current {
490 params = trial;
491 current = c_trial;
492 accepted = true;
493 step = local_step * 1.2;
495 break;
496 }
497 local_step *= 0.5;
498 }
499 history.push(current);
500 if !accepted {
501 break;
503 }
504 }
505
506 let solution = self.solution_state(¶ms)?;
507 Ok(VqlsResult {
508 params,
509 final_cost: current,
510 cost_history: history,
511 solution,
512 })
513 }
514}
515
516#[must_use]
518pub fn fidelity(u: &[Complex32], v: &[Complex32]) -> f64 {
519 let mut inner = Complex::<f64>::new(0.0, 0.0);
520 for (a, b) in u.iter().zip(v.iter()) {
521 let a64 = Complex::<f64>::new(a.re as f64, a.im as f64);
522 let b64 = Complex::<f64>::new(b.re as f64, b.im as f64);
523 inner += a64.conj() * b64;
524 }
525 inner.norm_sqr()
526}
527
528#[cfg(test)]
529mod tests {
530 use super::*;
531 use crate::handle::LcgRng;
532
533 fn c(re: f32) -> Complex32 {
534 Complex32::new(re, 0.0)
535 }
536
537 fn random_params(n: usize, seed: u64) -> Vec<f32> {
539 let mut rng = LcgRng::new(seed);
540 (0..n)
541 .map(|_| (rng.next_f32() * 2.0 - 1.0) * std::f32::consts::PI)
542 .collect()
543 }
544
545 #[test]
547 fn lcu_reconstructs_matrix() {
548 let terms = vec![
550 PauliTerm {
551 coeff: 0.6,
552 paulis: vec![Pauli::I],
553 },
554 PauliTerm {
555 coeff: 0.8,
556 paulis: vec![Pauli::X],
557 },
558 ];
559 let op = LcuOperator::new(&terms, 1).expect("valid 1-qubit LCU operator from I+X terms");
560 let m = op.matrix();
561 assert!((m[0] - c(0.6)).norm() < 1e-6, "[0][0]={:?}", m[0]);
562 assert!((m[1] - c(0.8)).norm() < 1e-6, "[0][1]={:?}", m[1]);
563 assert!((m[2] - c(0.8)).norm() < 1e-6, "[1][0]={:?}", m[2]);
564 assert!((m[3] - c(0.6)).norm() < 1e-6, "[1][1]={:?}", m[3]);
565
566 let terms2 = vec![
570 PauliTerm {
571 coeff: 1.0,
572 paulis: vec![Pauli::I, Pauli::Z], },
574 PauliTerm {
575 coeff: 0.5,
576 paulis: vec![Pauli::X, Pauli::X],
577 },
578 ];
579 let op2 = LcuOperator::new(&terms2, 2)
580 .expect("valid 2-qubit LCU operator from Z\u{2297}I and X\u{2297}X terms");
581 let m2 = op2.matrix();
582 let at = |row: usize, col: usize| m2[row * 4 + col];
583 assert!((at(0, 0) - c(1.0)).norm() < 1e-6);
586 assert!((at(1, 1) - c(1.0)).norm() < 1e-6);
587 assert!((at(2, 2) - c(-1.0)).norm() < 1e-6);
588 assert!((at(3, 3) - c(-1.0)).norm() < 1e-6);
589 assert!((at(0, 3) - c(0.5)).norm() < 1e-6, "00-11");
591 assert!((at(1, 2) - c(0.5)).norm() < 1e-6, "01-10");
592 }
593
594 #[test]
596 fn cost_is_non_negative() {
597 let terms = vec![
598 PauliTerm {
599 coeff: 0.7,
600 paulis: vec![Pauli::I],
601 },
602 PauliTerm {
603 coeff: 0.4,
604 paulis: vec![Pauli::X],
605 },
606 ];
607 let op = LcuOperator::new(&terms, 1).expect("valid 1-qubit LCU operator from I+X terms");
608 let b = vec![c(1.0), c(0.0)];
609 let ansatz = HardwareEfficientAnsatz::new(1, 2).expect("valid 1-qubit 2-layer ansatz");
610 let solver = VqlsSolver::new(op, &b, ansatz).expect("valid VQLS solver");
611 for seed in 0..20 {
612 let p = random_params(solver.ansatz.n_params(), seed);
613 let cost = solver.cost(&p).expect("cost evaluation should succeed");
614 assert!(cost >= -1e-9, "seed {seed}: cost {cost} negative");
615 assert!(cost <= 1.0 + 1e-9, "seed {seed}: cost {cost} > 1");
616 }
617 }
618
619 #[test]
621 fn exact_solution_gives_zero_cost() {
622 let terms = vec![
624 PauliTerm {
625 coeff: 0.6,
626 paulis: vec![Pauli::I],
627 },
628 PauliTerm {
629 coeff: 0.8,
630 paulis: vec![Pauli::X],
631 },
632 ];
633 let op = LcuOperator::new(&terms, 1).expect("valid 1-qubit LCU operator from I+X terms");
634 let b = vec![c(1.0), c(0.0)];
635 let ansatz = HardwareEfficientAnsatz::new(1, 1).expect("valid 1-qubit 1-layer ansatz");
636 let target = op
637 .classical_solution(&b)
638 .expect("classical solution should exist for invertible A");
639 let solver = VqlsSolver::new(op, &b, ansatz).expect("valid VQLS solver");
640 let cost = solver.cost_of_state(&target);
642 assert!(cost < 1e-6, "exact-solution cost {cost} should be ~0");
643 }
644
645 #[test]
647 fn optimized_state_has_high_fidelity() {
648 let terms = vec![
650 PauliTerm {
651 coeff: 0.8,
652 paulis: vec![Pauli::I],
653 },
654 PauliTerm {
655 coeff: 0.6,
656 paulis: vec![Pauli::Z],
657 },
658 ];
659 let op = LcuOperator::new(&terms, 1).expect("valid 1-qubit LCU operator from I+Z terms");
660 let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
661 let b = vec![c(inv_sqrt2), c(inv_sqrt2)];
662 let target = op
663 .classical_solution(&b)
664 .expect("classical solution should exist for invertible A");
665 let ansatz = HardwareEfficientAnsatz::new(1, 2).expect("valid 1-qubit 2-layer ansatz");
666 let solver = VqlsSolver::new(op, &b, ansatz).expect("valid VQLS solver");
667
668 let mut best_fid = 0.0_f64;
670 for seed in 0..6 {
671 let init = random_params(solver.ansatz.n_params(), 100 + seed);
672 let res = solver
673 .optimize(&init, 400, 0.5, 1e-3, 1e-8)
674 .expect("optimization should succeed");
675 let fid = fidelity(&res.solution, &target);
676 best_fid = best_fid.max(fid);
677 if best_fid > 0.95 {
678 break;
679 }
680 }
681 assert!(best_fid > 0.95, "best fidelity {best_fid} ≤ 0.95");
682 }
683
684 #[test]
686 fn cost_decreases_and_reaches_zero() {
687 let terms = vec![
688 PauliTerm {
689 coeff: 0.8,
690 paulis: vec![Pauli::I],
691 },
692 PauliTerm {
693 coeff: 0.6,
694 paulis: vec![Pauli::Z],
695 },
696 ];
697 let op = LcuOperator::new(&terms, 1).expect("valid 1-qubit LCU operator from I+Z terms");
698 let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
699 let b = vec![c(inv_sqrt2), c(inv_sqrt2)];
700 let ansatz = HardwareEfficientAnsatz::new(1, 2).expect("valid 1-qubit 2-layer ansatz");
701 let solver = VqlsSolver::new(op, &b, ansatz).expect("valid VQLS solver");
702
703 let mut converged = None;
705 for seed in 0..8 {
706 let init = random_params(solver.ansatz.n_params(), 7 + seed);
707 let res = solver
708 .optimize(&init, 400, 0.5, 1e-3, 1e-8)
709 .expect("optimization should succeed");
710 if res.final_cost < 1e-3 {
711 converged = Some(res);
712 break;
713 }
714 }
715 let res = converged.expect("at least one seed should converge near 0");
716 for w in res.cost_history.windows(2) {
718 assert!(w[1] <= w[0] + 1e-9, "cost increased: {} → {}", w[0], w[1]);
719 }
720 assert!(
721 res.final_cost < 1e-3,
722 "final cost {} not near 0",
723 res.final_cost
724 );
725 assert!(
727 res.cost_history[0] > res.final_cost,
728 "no progress: {} → {}",
729 res.cost_history[0],
730 res.final_cost
731 );
732 }
733
734 #[test]
736 fn identity_solution_is_b() {
737 let terms = vec![PauliTerm {
739 coeff: 1.0,
740 paulis: vec![Pauli::I],
741 }];
742 let op = LcuOperator::new(&terms, 1).expect("valid 1-qubit identity LCU operator");
743 let b = vec![c(0.3_f32.cos()), c(0.3_f32.sin())];
746 let target = op
747 .classical_solution(&b)
748 .expect("classical solution should exist for identity A");
749 let ansatz = HardwareEfficientAnsatz::new(1, 1).expect("valid 1-qubit 1-layer ansatz");
750 let solver = VqlsSolver::new(op, &b, ansatz).expect("valid VQLS solver");
751
752 let mut best_fid = 0.0_f64;
753 for seed in 0..6 {
754 let init = random_params(solver.ansatz.n_params(), 200 + seed);
755 let res = solver
756 .optimize(&init, 400, 0.5, 1e-3, 1e-9)
757 .expect("optimization should succeed");
758 best_fid = best_fid.max(fidelity(&res.solution, &target));
759 if best_fid > 0.98 {
760 break;
761 }
762 }
763 let bnorm: f32 = b.iter().map(|z| z.norm_sqr()).sum::<f32>().sqrt();
765 let b_normed: Vec<Complex32> = b.iter().map(|z| *z / bnorm).collect();
766 assert!(
767 fidelity(&target, &b_normed) > 0.999,
768 "target should equal |b⟩ for A=I"
769 );
770 assert!(best_fid > 0.98, "fidelity to |b⟩={best_fid}");
771 }
772
773 #[test]
775 fn two_qubit_solver_converges() {
776 let terms = vec![
779 PauliTerm {
780 coeff: 1.0,
781 paulis: vec![Pauli::I, Pauli::I],
782 },
783 PauliTerm {
784 coeff: 0.3,
785 paulis: vec![Pauli::Z, Pauli::I],
786 },
787 PauliTerm {
788 coeff: 0.2,
789 paulis: vec![Pauli::I, Pauli::Z],
790 },
791 ];
792 let op = LcuOperator::new(&terms, 2).expect("valid 2-qubit diagonal LCU operator");
793 let half = 0.5_f32;
794 let b = vec![c(half), c(half), c(half), c(half)];
795 let target = op
796 .classical_solution(&b)
797 .expect("classical solution should exist for diagonally dominant A");
798 let ansatz = HardwareEfficientAnsatz::new(2, 3).expect("valid 2-qubit 3-layer ansatz");
799 let solver = VqlsSolver::new(op, &b, ansatz).expect("valid VQLS solver");
800
801 let mut best_fid = 0.0_f64;
802 let mut best_cost = 1.0_f64;
803 for seed in 0..10 {
804 let init = random_params(solver.ansatz.n_params(), 300 + seed);
805 let res = solver
806 .optimize(&init, 500, 0.4, 1e-3, 1e-8)
807 .expect("optimization should succeed");
808 best_cost = best_cost.min(res.final_cost);
809 best_fid = best_fid.max(fidelity(&res.solution, &target));
810 if best_fid > 0.95 {
811 break;
812 }
813 }
814 assert!(best_cost < 1e-2, "2-qubit best cost {best_cost}");
815 assert!(best_fid > 0.95, "2-qubit best fidelity {best_fid}");
816 }
817
818 #[test]
820 fn validation_errors() {
821 assert!(LcuOperator::new(&[], 1).is_err());
823 let bad = vec![PauliTerm {
825 coeff: 1.0,
826 paulis: vec![Pauli::X, Pauli::X],
827 }];
828 assert!(LcuOperator::new(&bad, 1).is_err());
829 assert!(HardwareEfficientAnsatz::new(2, 0).is_err());
831 let op = LcuOperator::new(
833 &[PauliTerm {
834 coeff: 1.0,
835 paulis: vec![Pauli::I],
836 }],
837 1,
838 )
839 .expect("valid single-qubit identity LCU operator");
840 let ansatz = HardwareEfficientAnsatz::new(1, 1).expect("valid 1-qubit 1-layer ansatz");
841 assert!(VqlsSolver::new(op, &[c(1.0), c(0.0), c(0.0)], ansatz).is_err());
842 let op2 = LcuOperator::new(
844 &[PauliTerm {
845 coeff: 1.0,
846 paulis: vec![Pauli::I],
847 }],
848 1,
849 )
850 .expect("valid single-qubit identity LCU operator for zero-b test");
851 let ansatz2 = HardwareEfficientAnsatz::new(1, 1).expect("valid 1-qubit 1-layer ansatz");
852 assert!(VqlsSolver::new(op2, &[c(0.0), c(0.0)], ansatz2).is_err());
853 }
854}