From d674db7d0e9d309d0105e70d50291471c2eeb5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=A5=E5=81=A5?= Date: Wed, 1 Jul 2026 11:29:56 +0800 Subject: [PATCH 1/5] fix: resolve workspace clippy warnings Fix Clippy failures across core tests and Python bindings so the workspace passes with `-D warnings`. - Replace mechanical lint patterns in tests such as bool comparisons, needless range loops, redundant closures, unnecessary clones/casts, and assign-op patterns - Avoid approximate PI constants in tests where arbitrary numeric literals are sufficient - Move Python standard gate tests after all items to satisfy item ordering lint - Allow high-arity Python visualization wrappers locally to preserve the public Python API - Format affected visualization layout code --- .typos.toml | 8 ++ .../src/circuit/gate/standard.rs | 82 +++++++++---------- crates/binding-python/src/visualization.rs | 3 + .../src/circuit/ansatz/feature_map_test.rs | 4 +- .../ansatz/hamiltonian_evolution_test.rs | 2 +- crates/cqlib-core/src/circuit/circuit_test.rs | 4 +- crates/cqlib-core/src/circuit/depth.rs | 11 +-- .../cqlib-core/src/circuit/parameter_test.rs | 20 ++--- .../src/circuit/symbolic_matrix/gate_test.rs | 10 ++- .../circuit/symbolic_matrix/matrix_test.rs | 6 +- .../knowledge/builtin_rules_contract_test.rs | 13 +-- .../compile/knowledge/rule_dsl/dump_test.rs | 4 +- .../virtual_distillation_test.rs | 3 +- crates/cqlib-core/src/ir/qcis/dump_test.rs | 2 +- .../src/qis/state/density_matrix_test.rs | 18 ++-- .../src/qis/state/stabilizer_test.rs | 15 ++-- .../src/qis/state/statevector_test.rs | 4 +- crates/cqlib-core/src/util/test_utils.rs | 2 +- .../src/visualization/circuit/params_test.rs | 4 +- .../src/visualization/error_test.rs | 2 +- .../src/visualization/result/plot.rs | 3 +- .../src/visualization/test_utils.rs | 6 +- 22 files changed, 123 insertions(+), 103 deletions(-) diff --git a/.typos.toml b/.typos.toml index 36d18ed..ca2c9e9 100644 --- a/.typos.toml +++ b/.typos.toml @@ -9,3 +9,11 @@ IX = "IX" IZ = "IZ" XY = "XY" canonicalizer = "canonicalizer" +mch = "mch" +MCH = "MCH" +paeth = "paeth" +pngs = "pngs" +PNGs = "PNGs" + +[default.extend-identifiers] +PNGs = "PNGs" diff --git a/crates/binding-python/src/circuit/gate/standard.rs b/crates/binding-python/src/circuit/gate/standard.rs index 1299486..1541f81 100644 --- a/crates/binding-python/src/circuit/gate/standard.rs +++ b/crates/binding-python/src/circuit/gate/standard.rs @@ -336,47 +336,6 @@ impl PyStandardGate { } } -#[cfg(test)] -mod tests { - use super::PyStandardGate; - use cqlib_core::circuit::{Parameter, StandardGate}; - - #[test] - fn control_preserves_bound_parameters() { - let theta = Parameter::symbol("theta"); - let gate = PyStandardGate::from(StandardGate::RX, vec![theta.clone()]); - let controlled = gate.control(2); - - assert_eq!(controlled.inner.num_ctrl_qubits(), 2); - assert_eq!(controlled.params, vec![theta]); - } - - #[test] - fn inverse_uses_exact_bound_parameter_count() { - let theta = Parameter::symbol("theta"); - let gate = PyStandardGate::from(StandardGate::RX, vec![theta.clone()]); - let inverse = gate.inverse().unwrap(); - - assert_eq!(inverse.inner, StandardGate::RX); - assert_eq!(inverse.params, vec![-theta]); - } - - #[test] - fn all_mirrors_core_standard_gate_order() { - let gates = PyStandardGate::all(); - - assert_eq!(gates.len(), StandardGate::all().len()); - assert!( - gates - .iter() - .zip(StandardGate::all()) - .all( - |(py_gate, core_gate)| py_gate.inner == *core_gate && py_gate.params.is_empty() - ) - ); - } -} - /// Registers static gate attributes on the `StandardGate` class. /// /// Adds all standard gates (H, X, RX, CX, etc.) as class attributes @@ -460,3 +419,44 @@ impl PyStandardGate { } } } + +#[cfg(test)] +mod tests { + use super::PyStandardGate; + use cqlib_core::circuit::{Parameter, StandardGate}; + + #[test] + fn control_preserves_bound_parameters() { + let theta = Parameter::symbol("theta"); + let gate = PyStandardGate::from(StandardGate::RX, vec![theta.clone()]); + let controlled = gate.control(2); + + assert_eq!(controlled.inner.num_ctrl_qubits(), 2); + assert_eq!(controlled.params, vec![theta]); + } + + #[test] + fn inverse_uses_exact_bound_parameter_count() { + let theta = Parameter::symbol("theta"); + let gate = PyStandardGate::from(StandardGate::RX, vec![theta.clone()]); + let inverse = gate.inverse().unwrap(); + + assert_eq!(inverse.inner, StandardGate::RX); + assert_eq!(inverse.params, vec![-theta]); + } + + #[test] + fn all_mirrors_core_standard_gate_order() { + let gates = PyStandardGate::all(); + + assert_eq!(gates.len(), StandardGate::all().len()); + assert!( + gates + .iter() + .zip(StandardGate::all()) + .all( + |(py_gate, core_gate)| py_gate.inner == *core_gate && py_gate.params.is_empty() + ) + ); + } +} diff --git a/crates/binding-python/src/visualization.rs b/crates/binding-python/src/visualization.rs index 349021d..a66ed08 100644 --- a/crates/binding-python/src/visualization.rs +++ b/crates/binding-python/src/visualization.rs @@ -49,6 +49,7 @@ fn visualization_error_to_py_err(context: &str, err: VisualizationError) -> PyEr } /// Build result plot options from Python keyword arguments. +#[allow(clippy::too_many_arguments)] fn result_plot_options( figsize: Option<(f64, f64)>, color: Option>, @@ -235,6 +236,7 @@ pub fn py_draw_figure( output_path = None ) )] +#[allow(clippy::too_many_arguments)] pub fn py_plot_histogram( result: &PyExecutionResult, figsize: Option<(f64, f64)>, @@ -280,6 +282,7 @@ pub fn py_plot_histogram( output_path = None ) )] +#[allow(clippy::too_many_arguments)] pub fn py_plot_distribution( result: &PyExecutionResult, figsize: Option<(f64, f64)>, diff --git a/crates/cqlib-core/src/circuit/ansatz/feature_map_test.rs b/crates/cqlib-core/src/circuit/ansatz/feature_map_test.rs index 389a439..29487c7 100644 --- a/crates/cqlib-core/src/circuit/ansatz/feature_map_test.rs +++ b/crates/cqlib-core/src/circuit/ansatz/feature_map_test.rs @@ -684,8 +684,8 @@ fn test_pauli_feature_map_full_entanglement_3qubits() { assert_eq!(ops.len(), 12); // Check H gates - for i in 0..3 { - assert!(is_h_gate_on_qubit(&ops[i], i)); + for (i, op) in ops.iter().enumerate().take(3) { + assert!(is_h_gate_on_qubit(op, i)); } } diff --git a/crates/cqlib-core/src/circuit/ansatz/hamiltonian_evolution_test.rs b/crates/cqlib-core/src/circuit/ansatz/hamiltonian_evolution_test.rs index 6a09877..80895c4 100644 --- a/crates/cqlib-core/src/circuit/ansatz/hamiltonian_evolution_test.rs +++ b/crates/cqlib-core/src/circuit/ansatz/hamiltonian_evolution_test.rs @@ -141,7 +141,7 @@ fn matrix_exp_iht(h_mat: &Array2, t: f64) -> Array2 { for k in 1..=60usize { term = term.dot(&a) / Complex64::from(k as f64); - result = result + &term; + result += &term; // Early termination if converged if term.iter().all(|v| v.norm() < 1e-16) { diff --git a/crates/cqlib-core/src/circuit/circuit_test.rs b/crates/cqlib-core/src/circuit/circuit_test.rs index 0fe00e9..4798c7a 100644 --- a/crates/cqlib-core/src/circuit/circuit_test.rs +++ b/crates/cqlib-core/src/circuit/circuit_test.rs @@ -677,8 +677,8 @@ fn test_assign_parameters() { if let CircuitParam::Index(idx) = assigned_circuit.data[1].params[0] { let param = &assigned_circuit.parameters[idx as usize]; let symbols = param.get_symbols(); - assert!(symbols.contains(&"b".to_string())); - assert!(!symbols.contains(&"a".to_string())); + assert!(symbols.contains("b")); + assert!(!symbols.contains("a")); } else { panic!( "Expected Index for rz, got {:?}", diff --git a/crates/cqlib-core/src/circuit/depth.rs b/crates/cqlib-core/src/circuit/depth.rs index 495beae..49282ba 100644 --- a/crates/cqlib-core/src/circuit/depth.rs +++ b/crates/cqlib-core/src/circuit/depth.rs @@ -233,8 +233,8 @@ fn for_loop_iterations(op: &ForOp) -> Option { } let span = stop.checked_sub(start)?; // ceil(span / step) = (span + step - 1) / step, guarded against overflow. - let numer = span.checked_add(step.checked_sub(1)?)?; - let iterations = numer.checked_div(step)?; + let numerator = span.checked_add(step.checked_sub(1)?)?; + let iterations = numerator.checked_div(step)?; Some(iterations as usize) } else { // Descending range: step < 0. Iterate while start > stop. @@ -243,8 +243,8 @@ fn for_loop_iterations(op: &ForOp) -> Option { } let span = start.checked_sub(stop)?; let abs_step = (0u128).checked_sub(step)?; // |step|, step < 0 so this is positive - let numer = span.checked_add(abs_step.checked_sub(1)?)?; - let iterations = numer.checked_div(abs_step)?; + let numerator = span.checked_add(abs_step.checked_sub(1)?)?; + let iterations = numerator.checked_div(abs_step)?; Some(iterations as usize) } } @@ -323,6 +323,7 @@ mod tests { Qubit, }; use smallvec::smallvec; + use std::slice::from_ref; fn q(n: u32) -> Qubit { Qubit::new(n) @@ -710,7 +711,7 @@ mod tests { params: smallvec![], label: None, }; - assert!(!contains_control_flow(&[plain.clone()])); + assert!(!contains_control_flow(from_ref(&plain))); let if_op = IfOp::new( ClassicalExpr::bool_literal(true), diff --git a/crates/cqlib-core/src/circuit/parameter_test.rs b/crates/cqlib-core/src/circuit/parameter_test.rs index 48a3380..bb7b4b8 100644 --- a/crates/cqlib-core/src/circuit/parameter_test.rs +++ b/crates/cqlib-core/src/circuit/parameter_test.rs @@ -21,8 +21,8 @@ fn test_p_construction() { assert_eq!(p1.to_string(), "theta"); // From f64 - let p2 = Parameter::from(3.14); - assert_eq!(p2.to_string(), "3.14"); + let p2 = Parameter::from(2.5); + assert_eq!(p2.to_string(), "2.5"); // From integer let p3 = Parameter::from(42); @@ -555,8 +555,8 @@ fn test_p_derivative_constant() { let d_c1 = c1.derivative(x).unwrap(); assert_eq!(d_c1, Parameter::from(0)); - // d(3.14)/dx = 0 - let c2 = Parameter::from(3.14); + // d(2.5)/dx = 0 + let c2 = Parameter::from(2.5); let d_c2 = c2.derivative(x).unwrap(); assert_eq!(d_c2, Parameter::from(0)); @@ -833,8 +833,8 @@ fn test_p_parse_number() { let p = Parameter::try_from("1.0").unwrap(); assert!((p.evaluate(&None).unwrap() - 1.0).abs() < 1e-10); - let p = Parameter::try_from("3.14").unwrap(); - assert!((p.evaluate(&None).unwrap() - 3.14).abs() < 1e-10); + let p = Parameter::try_from("2.5").unwrap(); + assert!((p.evaluate(&None).unwrap() - 2.5).abs() < 1e-10); let p = Parameter::try_from("42").unwrap(); assert!((p.evaluate(&None).unwrap() - 42.0).abs() < 1e-10); @@ -979,15 +979,15 @@ fn test_p_display_format() { #[test] fn test_p_complex_real_world() { - // RXY gate parameters: theta = pi/2 + 1, phi = 3.14 + // RXY gate parameters: theta = pi/2 + 1, phi = 2.5 let theta = Parameter::try_from("pi/2+1").unwrap(); - let phi = Parameter::try_from("3.14").unwrap(); + let phi = Parameter::try_from("2.5").unwrap(); let mut bindings = HashMap::new(); bindings.insert("pi", PI); assert!((theta.evaluate(&Some(bindings.clone())).unwrap() - (PI / 2.0 + 1.0)).abs() < 1e-10); - assert!((phi.evaluate(&None).unwrap() - 3.14).abs() < 1e-10); + assert!((phi.evaluate(&None).unwrap() - 2.5).abs() < 1e-10); // RZ(pi/4 + 0.5) let rz_param = Parameter::try_from("pi/4+0.5").unwrap(); @@ -1105,7 +1105,7 @@ fn test_from_str() { #[test] fn test_is_constant() { - let constant = Parameter::from(3.14); + let constant = Parameter::from(2.5); assert!(constant.is_constant()); let symbolic = Parameter::symbol("x"); diff --git a/crates/cqlib-core/src/circuit/symbolic_matrix/gate_test.rs b/crates/cqlib-core/src/circuit/symbolic_matrix/gate_test.rs index f23c606..0a9a824 100644 --- a/crates/cqlib-core/src/circuit/symbolic_matrix/gate_test.rs +++ b/crates/cqlib-core/src/circuit/symbolic_matrix/gate_test.rs @@ -167,7 +167,7 @@ fn test_parametric_standard_gates_with_symbolic_params_match_numeric() { StandardGate::XY2P, StandardGate::XY2M, ] { - let symbolic = standard_gate_symbolic_matrix(gate, &[theta.clone()]).unwrap(); + let symbolic = standard_gate_symbolic_matrix(gate, std::slice::from_ref(&theta)).unwrap(); let mut bindings = HashMap::new(); bindings.insert("theta", 0.63); let evaluated = evaluate_symbolic_matrix(&symbolic, &Some(bindings.clone())).unwrap(); @@ -1211,7 +1211,13 @@ fn test_apply_standard_gate_to_matrix_directly() { // Symbolic path: RX(theta) on qubit 1. let theta = Parameter::symbol("theta"); let mut matrix = symbolic_eye(4); - apply_standard_gate_to_matrix(&mut matrix, StandardGate::RX, &[1], &[theta.clone()]).unwrap(); + apply_standard_gate_to_matrix( + &mut matrix, + StandardGate::RX, + &[1], + std::slice::from_ref(&theta), + ) + .unwrap(); let mut bindings = HashMap::new(); bindings.insert("theta", PI / 4.0); let evaluated = evaluate_symbolic_matrix(&matrix, &Some(bindings.clone())).unwrap(); diff --git a/crates/cqlib-core/src/circuit/symbolic_matrix/matrix_test.rs b/crates/cqlib-core/src/circuit/symbolic_matrix/matrix_test.rs index e245c7f..1c2a972 100644 --- a/crates/cqlib-core/src/circuit/symbolic_matrix/matrix_test.rs +++ b/crates/cqlib-core/src/circuit/symbolic_matrix/matrix_test.rs @@ -53,11 +53,11 @@ fn test_symbolic_complex_exp_i() { #[test] fn test_symbolic_complex_from_real() { - let z = SymbolicComplex::from_real(3.14); + let z = SymbolicComplex::from_real(2.5); assert!(!z.is_zero_exact()); assert!(z.im.is_zero()); let evaluated = z.evaluate(&None).unwrap(); - assert!((evaluated.re - 3.14).abs() < 1e-12); + assert!((evaluated.re - 2.5).abs() < 1e-12); assert!(evaluated.im.abs() < 1e-12); } @@ -132,7 +132,7 @@ fn test_symbolic_complex_complex64_mul() { #[test] fn test_symbolic_complex_display() { - let real = SymbolicComplex::from_real(3.14); + let real = SymbolicComplex::from_real(2.5); assert!(!real.to_string().contains('i')); let imag = SymbolicComplex::new(0.0, 2.0); diff --git a/crates/cqlib-core/src/compile/knowledge/builtin_rules_contract_test.rs b/crates/cqlib-core/src/compile/knowledge/builtin_rules_contract_test.rs index c104f0f..a9a164e 100644 --- a/crates/cqlib-core/src/compile/knowledge/builtin_rules_contract_test.rs +++ b/crates/cqlib-core/src/compile/knowledge/builtin_rules_contract_test.rs @@ -325,10 +325,9 @@ fn decompose_cy_to_rzz_formula_matches_gate_definition() { #[test] fn new_rzz_native_lowering_rules_pass_layered_verify() { let library = RuleLibrary::builtin_rules().unwrap(); - for name in ["decompose_cy_to_rzz"] { - let rule = library.get_by_name(name).expect(name); - assert_rule_verification_passes(rule); - } + let name = "decompose_cy_to_rzz"; + let rule = library.get_by_name(name).expect(name); + assert_rule_verification_passes(rule); } #[test] @@ -355,7 +354,8 @@ fn ion_trap_direct_ising_rules_pass_layered_verify() { #[test] fn ion_trap_direct_ccx_rules_pass_layered_verify() { let library = RuleLibrary::builtin_rules().unwrap(); - for name in ["decompose_ccx_to_rx_ry_rzz"] { + { + let name = "decompose_ccx_to_rx_ry_rzz"; let rule = library.get_by_name(name).expect(name); assert_rule_verification_passes(rule); } @@ -541,7 +541,8 @@ fn ion_trap_rzz_intermediate_rules_are_present() { #[test] fn documented_missing_rules_for_rzz_native_targets() { let library = RuleLibrary::builtin_rules().unwrap(); - for missing in ["decompose_ms_to_rzz"] { + { + let missing = "decompose_ms_to_rzz"; assert!( library.get_by_name(missing).is_none(), "rule `{missing}` is not implemented yet (documented gap)" diff --git a/crates/cqlib-core/src/compile/knowledge/rule_dsl/dump_test.rs b/crates/cqlib-core/src/compile/knowledge/rule_dsl/dump_test.rs index b7c7e90..9a038e6 100644 --- a/crates/cqlib-core/src/compile/knowledge/rule_dsl/dump_test.rs +++ b/crates/cqlib-core/src/compile/knowledge/rule_dsl/dump_test.rs @@ -130,7 +130,7 @@ fn dump_gphase_roundtrip() { let dumped = rules .iter() - .map(|r| dump_rule_to_string(r)) + .map(dump_rule_to_string) .collect::>() .join("\n"); let reparsed = load_rules_from_str(&dumped).unwrap(); @@ -159,7 +159,7 @@ fn dump_multi_controlled_gate_roundtrip() { let dumped = rules .iter() - .map(|r| dump_rule_to_string(r)) + .map(dump_rule_to_string) .collect::>() .join("\n"); assert!(dumped.contains("MCX[3] 0 1 2 3")); diff --git a/crates/cqlib-core/src/error_mitigation/virtual_distillation_test.rs b/crates/cqlib-core/src/error_mitigation/virtual_distillation_test.rs index 6060ed5..e9d9cc3 100644 --- a/crates/cqlib-core/src/error_mitigation/virtual_distillation_test.rs +++ b/crates/cqlib-core/src/error_mitigation/virtual_distillation_test.rs @@ -200,8 +200,7 @@ fn test_run_vd_returns_mu_and_var() { Instruction::Standard(StandardGate::SWAP) )); - if hamiltonian_arg.is_some() { - let expanded_hamiltonian = hamiltonian_arg.unwrap(); + if let Some(expanded_hamiltonian) = hamiltonian_arg { assert_eq!(expanded_hamiltonian.num_qubits, 2); assert_eq!(expanded_hamiltonian.terms.len(), 1); let (term, _coeff) = &expanded_hamiltonian.terms[0]; diff --git a/crates/cqlib-core/src/ir/qcis/dump_test.rs b/crates/cqlib-core/src/ir/qcis/dump_test.rs index 10352a1..db38f4c 100644 --- a/crates/cqlib-core/src/ir/qcis/dump_test.rs +++ b/crates/cqlib-core/src/ir/qcis/dump_test.rs @@ -301,7 +301,7 @@ fn test_format_float() { assert_eq!(format_float(-std::f64::consts::PI / 2.0), "-pi/2"); assert_eq!(format_float(std::f64::consts::PI / 4.0), "pi/4"); assert_eq!(format_float(42.0), "42"); - assert_eq!(format_float(3.14159), "3.14159"); + assert_eq!(format_float(2.5), "2.5"); } #[test] diff --git a/crates/cqlib-core/src/qis/state/density_matrix_test.rs b/crates/cqlib-core/src/qis/state/density_matrix_test.rs index deb59b2..22b24e2 100644 --- a/crates/cqlib-core/src/qis/state/density_matrix_test.rs +++ b/crates/cqlib-core/src/qis/state/density_matrix_test.rs @@ -749,13 +749,13 @@ fn test_measure_out_of_bounds() { #[test] fn test_measure_deterministic_zero_and_one() { let mut zero = DensityMatrix::new(1); - assert_eq!(zero.measure(0).unwrap(), false); + assert!(!zero.measure(0).unwrap()); assert_relative_eq!(zero.data[0].re, 1.0); assert_relative_eq!(zero.data[3].re, 0.0); let mut one = DensityMatrix::new(1); one.apply_x(0).unwrap(); - assert_eq!(one.measure(0).unwrap(), true); + assert!(one.measure(0).unwrap()); assert_relative_eq!(one.data[0].re, 0.0); assert_relative_eq!(one.data[3].re, 1.0); } @@ -912,8 +912,8 @@ fn test_psd_gershgorin_false_negative_equal_superposition() { let size = dim * dim; let mut data = vec![Complex64::new(0.0, 0.0); size]; let val = Complex64::new(0.25, 0.0); - for i in 0..size { - data[i] = val; + for item in data.iter_mut().take(size) { + *item = val; } let dm = DensityMatrix { data, @@ -943,9 +943,9 @@ fn test_psd_valid_pure_non_diagonally_dominant() { let sqrt_06 = 0.6_f64.sqrt(); let sqrt_04 = 0.4_f64.sqrt(); let mut data = vec![Complex64::new(0.0, 0.0); size]; - data[0 * dim + 0] = Complex64::new(0.6, 0.0); - data[0 * dim + 3] = Complex64::new(sqrt_06 * sqrt_04, 0.0); - data[3 * dim + 0] = Complex64::new(sqrt_06 * sqrt_04, 0.0); + data[0] = Complex64::new(0.6, 0.0); + data[3] = Complex64::new(sqrt_06 * sqrt_04, 0.0); + data[3 * dim] = Complex64::new(sqrt_06 * sqrt_04, 0.0); data[3 * dim + 3] = Complex64::new(0.4, 0.0); let dm = DensityMatrix { data, @@ -1015,7 +1015,7 @@ fn test_psd_negative_eigenvalue_rejected() { let size = dim * dim; let mut data = vec![Complex64::new(0.0, 0.0); size]; data[0] = Complex64::new(-0.1, 0.0); - data[1 * dim + 1] = Complex64::new(1.1, 0.0); + data[dim + 1] = Complex64::new(1.1, 0.0); let dm = DensityMatrix { data, num_qubits: n, @@ -1038,7 +1038,7 @@ fn test_psd_tiny_negative_eigenvalue_accepted_with_tolerance() { let eps = 1e-12_f64; let mut data = vec![Complex64::new(0.0, 0.0); size]; data[0] = Complex64::new(1.0 + eps, 0.0); - data[1 * dim + 1] = Complex64::new(-eps, 0.0); + data[dim + 1] = Complex64::new(-eps, 0.0); let dm = DensityMatrix { data, num_qubits: n, diff --git a/crates/cqlib-core/src/qis/state/stabilizer_test.rs b/crates/cqlib-core/src/qis/state/stabilizer_test.rs index af6dd90..66ff67c 100644 --- a/crates/cqlib-core/src/qis/state/stabilizer_test.rs +++ b/crates/cqlib-core/src/qis/state/stabilizer_test.rs @@ -187,7 +187,8 @@ fn test_from_circuit_rejects_non_clifford_gate_set() { use num_complex::Complex64; use std::f64::consts::PI; - let builders: Vec<(&str, Box)> = vec![ + type CircuitBuilder = (&'static str, Box); + let builders: Vec = vec![ ( "T", Box::new(|c| { @@ -408,7 +409,7 @@ fn test_measure_statistics_approx_50_50() { } // Generous bounds: 30% to 70% assert!( - ones >= 60 && ones <= 140, + (60..=140).contains(&ones), "Expected ~50% ones, got {ones}/{total}" ); } @@ -910,11 +911,11 @@ fn test_repeated_deterministic_measurements() { let mut s = StabilizerState::new(4); // First pass: all deterministically 0 for q in 0..4 { - assert_eq!(s.measure(q).unwrap(), false, "qubit {q} first pass"); + assert!(!s.measure(q).unwrap(), "qubit {q} first pass"); } // Second pass: state now collapsed but still |0000⟩, all deterministic for q in 0..4 { - assert_eq!(s.measure(q).unwrap(), false, "qubit {q} second pass"); + assert!(!s.measure(q).unwrap(), "qubit {q} second pass"); } } @@ -1087,7 +1088,7 @@ fn test_reset_from_one() { let mut s = StabilizerState::new(1); s.apply_x(0).unwrap(); // |0⟩ → |1⟩ s.reset(0).unwrap(); // → |0⟩ - assert_eq!(s.measure(0).unwrap(), false, "after reset should be |0⟩"); + assert!(!s.measure(0).unwrap(), "after reset should be |0⟩"); } /// reset() on |0⟩ is a no-op. @@ -1095,7 +1096,7 @@ fn test_reset_from_one() { fn test_reset_from_zero() { let mut s = StabilizerState::new(1); s.reset(0).unwrap(); - assert_eq!(s.measure(0).unwrap(), false); + assert!(!s.measure(0).unwrap()); } /// reset() on a superposition state always yields |0⟩. @@ -1105,7 +1106,7 @@ fn test_reset_from_superposition() { let mut s = StabilizerState::new(1); s.apply_h(0).unwrap(); // |+⟩ random collapse s.reset(0).unwrap(); // must always be |0⟩ after - assert_eq!(s.measure(0).unwrap(), false); + assert!(!s.measure(0).unwrap()); } } diff --git a/crates/cqlib-core/src/qis/state/statevector_test.rs b/crates/cqlib-core/src/qis/state/statevector_test.rs index 8b6b570..33dcd77 100644 --- a/crates/cqlib-core/src/qis/state/statevector_test.rs +++ b/crates/cqlib-core/src/qis/state/statevector_test.rs @@ -1976,13 +1976,13 @@ fn test_measure_out_of_bounds() { #[test] fn test_measure_deterministic_zero_and_one() { let mut zero = Statevector::new(1); - assert_eq!(zero.measure(0).unwrap(), false); + assert!(!zero.measure(0).unwrap()); assert_complex_eq(zero.data[0], c(1.0, 0.0), "|0> remains |0>"); assert_complex_eq(zero.data[1], c(0.0, 0.0), "|1> amplitude remains zero"); let mut one = Statevector::new(1); one.apply_x(0).unwrap(); - assert_eq!(one.measure(0).unwrap(), true); + assert!(one.measure(0).unwrap()); assert_complex_eq(one.data[0], c(0.0, 0.0), "|0> amplitude remains zero"); assert_complex_eq(one.data[1], c(1.0, 0.0), "|1> remains |1>"); } diff --git a/crates/cqlib-core/src/util/test_utils.rs b/crates/cqlib-core/src/util/test_utils.rs index 2520bd2..70881ee 100644 --- a/crates/cqlib-core/src/util/test_utils.rs +++ b/crates/cqlib-core/src/util/test_utils.rs @@ -609,7 +609,7 @@ pub fn contains_high_level_gate(circuit: &Circuit) -> bool { }) } -/// Returns whether a named compiler workflow step reported a change. +// Returns whether a named compiler workflow step reported a change. // pub fn step_changed(result: &CompileResult, name: &str) -> bool { // result // .steps diff --git a/crates/cqlib-core/src/visualization/circuit/params_test.rs b/crates/cqlib-core/src/visualization/circuit/params_test.rs index 77feddc..fc52f06 100644 --- a/crates/cqlib-core/src/visualization/circuit/params_test.rs +++ b/crates/cqlib-core/src/visualization/circuit/params_test.rs @@ -377,7 +377,7 @@ fn test_formatter_pi_max_denominator_limit() { #[test] fn test_parameter_display_mode_clone_copy() { let mode = ParameterDisplayMode::Numeric; - let mode_clone = mode.clone(); + let mode_clone = mode; assert_eq!(mode, mode_clone); let mode_copy = mode; @@ -387,7 +387,7 @@ fn test_parameter_display_mode_clone_copy() { #[test] fn test_parameter_format_options_clone_copy() { let opts = ParameterFormatOptions::default(); - let opts_clone = opts.clone(); + let opts_clone = opts; assert_eq!(opts.mode, opts_clone.mode); let opts_copy = opts; diff --git a/crates/cqlib-core/src/visualization/error_test.rs b/crates/cqlib-core/src/visualization/error_test.rs index e0b74ec..dc06446 100644 --- a/crates/cqlib-core/src/visualization/error_test.rs +++ b/crates/cqlib-core/src/visualization/error_test.rs @@ -113,7 +113,7 @@ fn test_all_error_variants() { VisualizationError::UnknownQubit(0), VisualizationError::ParameterIndexOutOfBounds { index: 1, len: 1 }, VisualizationError::SvgRenderFailed("render error".to_string()), - VisualizationError::Io(io::Error::new(io::ErrorKind::Other, "other")), + VisualizationError::Io(io::Error::other("other")), ]; for err in errors { diff --git a/crates/cqlib-core/src/visualization/result/plot.rs b/crates/cqlib-core/src/visualization/result/plot.rs index dbb1672..74481e1 100644 --- a/crates/cqlib-core/src/visualization/result/plot.rs +++ b/crates/cqlib-core/src/visualization/result/plot.rs @@ -308,7 +308,8 @@ fn bar_chart_layout(plot: &PreparedResultPlot, options: &ResultPlotOptions) -> B // The left gutter must hold, from the y-axis outward: the tick gap, the tick // labels, a title pad, the rotated y-axis title's width, and a left edge pad. // This mirrors how matplotlib's `tight_layout` reserves room for the title. - let margin_left = (TICK_GAP + max_tick_w + TITLE_PAD + 2.0 * TITLE_HALF + LEFT_PAD).clamp(48.0, 110.0); + let margin_left = + (TICK_GAP + max_tick_w + TITLE_PAD + 2.0 * TITLE_HALF + LEFT_PAD).clamp(48.0, 110.0); let right_legend_margin = options.legend.as_ref().map_or(0.0, |legend| { legend diff --git a/crates/cqlib-core/src/visualization/test_utils.rs b/crates/cqlib-core/src/visualization/test_utils.rs index 9fafd9f..20bbf9b 100644 --- a/crates/cqlib-core/src/visualization/test_utils.rs +++ b/crates/cqlib-core/src/visualization/test_utils.rs @@ -142,9 +142,9 @@ fn save_diff_png( for idx in 0..(width as usize * height as usize) { let i3 = idx * 3; let i4 = idx * 4; - let dr = (i16::from(a[i3]) - i16::from(b[i3])).unsigned_abs() as u16; - let dg = (i16::from(a[i3 + 1]) - i16::from(b[i3 + 1])).unsigned_abs() as u16; - let db = (i16::from(a[i3 + 2]) - i16::from(b[i3 + 2])).unsigned_abs() as u16; + let dr = (i16::from(a[i3]) - i16::from(b[i3])).unsigned_abs(); + let dg = (i16::from(a[i3 + 1]) - i16::from(b[i3 + 1])).unsigned_abs(); + let db = (i16::from(a[i3 + 2]) - i16::from(b[i3 + 2])).unsigned_abs(); dst[i4] = (dr.saturating_mul(AMP).min(255)) as u8; dst[i4 + 1] = (dg.saturating_mul(AMP).min(255)) as u8; dst[i4 + 2] = (db.saturating_mul(AMP).min(255)) as u8; From d993fbc1f64e4157a318885cb8e8ed4ac4bb4488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=A5=E5=81=A5?= Date: Wed, 1 Jul 2026 14:16:44 +0800 Subject: [PATCH 2/5] chore(deps): update PyO3 dependencies for cargo audit Upgrade pyo3 and numpy to 0.29 to resolve RustSec advisories for pyo3 0.27. Update Cargo.lock with compatible dependency versions, including memmap2 0.9.11. --- Cargo.lock | 83 ++++++++++++-------------------- Cargo.toml | 4 +- crates/binding-python/Cargo.toml | 2 +- 3 files changed, 35 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea918a4..3f4edf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,9 +697,9 @@ dependencies = [ [[package]] name = "faer" -version = "0.24.1" +version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eda5a09c282d3b6f73881446d031a77e36fbf1e3d67bdc5085149b7e6b3f631" +checksum = "5ab6df3dd147fe8d702a288b95bcd8fcc499ab572fc80da6828f60cd4d524d67" dependencies = [ "bytemuck", "dyn-stack", @@ -1149,15 +1149,6 @@ dependencies = [ "rayon", ] -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - [[package]] name = "interpol" version = "0.2.1" @@ -1212,9 +1203,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -1352,9 +1343,9 @@ checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -1601,9 +1592,9 @@ dependencies = [ [[package]] name = "numpy" -version = "0.27.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aac2e6a6e4468ffa092ad43c39b81c79196c2bb773b8db4085f695efe3bba17" +checksum = "6a5b15d63a5ff39e378daed0e1340d3a5964703ea9712eb09a0dc66fade996f4" dependencies = [ "libc", "ndarray 0.17.2", @@ -1973,37 +1964,34 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.27.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d" +checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c" dependencies = [ "chrono", - "indoc", "libc", - "memoffset", "num-complex", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", - "unindent", ] [[package]] name = "pyo3-build-config" -version = "0.27.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6" +checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078" dependencies = [ "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.27.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089" +checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b" dependencies = [ "libc", "pyo3-build-config", @@ -2011,9 +1999,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.27.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02" +checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -2023,13 +2011,12 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.27.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9" +checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362" dependencies = [ "heck", "proc-macro2", - "pyo3-build-config", "quote", "syn 2.0.118", ] @@ -2060,9 +2047,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -2744,9 +2731,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.49" +version = "0.3.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" +checksum = "0e48db7b415311b615f910b3dcaa4557bcd4bf1982379c95c223fd8c2a20e210" dependencies = [ "deranged", "num-conv", @@ -2977,12 +2964,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unindent" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" - [[package]] name = "usvg" version = "0.45.1" @@ -3018,9 +2999,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.3" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -3076,9 +3057,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -3089,9 +3070,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3099,9 +3080,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", @@ -3112,9 +3093,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] diff --git a/Cargo.toml b/Cargo.toml index f204d5b..8d7b359 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,14 +23,14 @@ strip = true approx = { version = "0.5", features = ["num-complex"] } bitvec = "1.0.1" cbindgen = "0.29.2" -faer = "0.24.0" +faer = "0.24" lalrpop = "0.22" lalrpop-util = "0.22" lazy_static = "1.5.0" indexmap = "2.12" ndarray = { version = "0.17", features = ["approx", "rayon"] } num = "0.4" -numpy = "0.27" +numpy = "0.29" num-complex = "0.4" oq3_semantics = "0.7.0" rayon = "1.11.0" diff --git a/crates/binding-python/Cargo.toml b/crates/binding-python/Cargo.toml index c40d51b..1bdc06e 100644 --- a/crates/binding-python/Cargo.toml +++ b/crates/binding-python/Cargo.toml @@ -11,7 +11,7 @@ name = "_native" crate-type = ["cdylib", "rlib"] [dependencies] -pyo3 = { version = "0.27", features = ["abi3-py310", "num-complex", "chrono"] } +pyo3 = { version = "0.29", features = ["abi3-py310", "num-complex", "chrono"] } chrono = "0.4" cqlib-core = { path = "../cqlib-core", version = '0.1.0' } num-complex.workspace = true From 2f86d2222d11e171a256b468a147d7685d5c48af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=A5=E5=81=A5?= Date: Wed, 1 Jul 2026 14:26:54 +0800 Subject: [PATCH 3/5] fix(binding-c): link platform native libs for CMake staticlib example Explicitly link the native libraries required by the Rust staticlib when building the C example through CMake. - Use the C++ linker and link libc++ on macOS for Rust/libc++ symbols - Link ntdll and Rust std related system libraries on Windows MSVC - Keep Linux native library linkage scoped to non-Apple Unix platforms --- crates/binding-c/CMakeLists.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/binding-c/CMakeLists.txt b/crates/binding-c/CMakeLists.txt index 2a9d283..40737c9 100644 --- a/crates/binding-c/CMakeLists.txt +++ b/crates/binding-c/CMakeLists.txt @@ -42,13 +42,16 @@ add_dependencies(cqlib_c_example cargo_build_binding_c) target_include_directories(cqlib_c_example PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(cqlib_c_example PRIVATE ${RUST_LIB}) -if(UNIX) +if(APPLE) + enable_language(CXX) + set_target_properties(cqlib_c_example PROPERTIES LINKER_LANGUAGE CXX) + target_link_libraries(cqlib_c_example PRIVATE c++ "-framework CoreFoundation") +endif() + +if(UNIX AND NOT APPLE) target_link_libraries(cqlib_c_example PRIVATE pthread dl m) - if(APPLE) - target_link_libraries(cqlib_c_example PRIVATE "-framework CoreFoundation") - endif() endif() if(WIN32) - target_link_libraries(cqlib_c_example PRIVATE ws2_32 userenv advapi32 bcrypt) + target_link_libraries(cqlib_c_example PRIVATE ntdll ws2_32 bcrypt userenv advapi32) endif() From 8a9a74df536baef7da4e3f327655275ade0426b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=A5=E5=81=A5?= Date: Wed, 1 Jul 2026 14:41:27 +0800 Subject: [PATCH 4/5] docs(python): correct standard gate count in circuit gates module --- crates/binding-python/cqlib/circuit/gates/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/binding-python/cqlib/circuit/gates/__init__.py b/crates/binding-python/cqlib/circuit/gates/__init__.py index aae734f..2d70736 100644 --- a/crates/binding-python/cqlib/circuit/gates/__init__.py +++ b/crates/binding-python/cqlib/circuit/gates/__init__.py @@ -15,8 +15,8 @@ The ``cqlib.circuit.gates`` submodule provides gate type classes (:class:`StandardGate`, :class:`UnitaryGate`, :class:`MCGate`, :class:`CircuitGate`, :class:`Directive`, :class:`FrozenCircuit`) and -33 pre-built standard-gate singleton constants (``H``, ``X``, ``CX``, -``RX``, ``RX``, etc.) for use with +36 pre-built standard-gate singleton constants (``H``, ``X``, ``CX``, +``RX``, ``RZ``, etc.) for use with :meth:`Circuit.append_gate ` and :meth:`Circuit.append_unitary_gate `. From 58694d0287df3200fe8f78572c6191b624a8cbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=A5=E5=81=A5?= Date: Wed, 1 Jul 2026 15:07:46 +0800 Subject: [PATCH 5/5] fix(python): opt into explicit PyO3 pyclass extraction behavior Add explicit `from_py_object` or `skip_from_py_object` options to Clone-backed Python binding classes for PyO3 0.29. Types that are accepted as owned Python API arguments or extracted via `FromPyObject` keep the previous behavior with `from_py_object`. Types that are only returned, borrowed, or used as Python-side handles now use `skip_from_py_object` to avoid unnecessary implicit extraction and clone semantics. This resolves the PyO3 deprecation warnings promoted by `-D warnings` under clippy. --- .../src/circuit/ansatz/feature_map.rs | 36 ++++++++++--- .../circuit/ansatz/hamiltonian_evolution.rs | 19 +++++-- .../src/circuit/ansatz/layers.rs | 12 ++++- .../binding-python/src/circuit/ansatz/qaoa.rs | 6 ++- .../src/circuit/ansatz/two_local.rs | 12 ++++- crates/binding-python/src/circuit/bit.rs | 2 +- .../src/circuit/circuit_impl.rs | 2 +- .../binding-python/src/circuit/classical.rs | 10 ++-- .../src/circuit/classical_expr.rs | 2 +- .../src/circuit/control_flow.rs | 8 +-- .../src/circuit/gate/circuit_gate.rs | 4 +- .../src/circuit/gate/directive.rs | 2 +- .../src/circuit/gate/mc_gate.rs | 2 +- .../src/circuit/gate/standard.rs | 2 +- .../src/circuit/gate/unitary.rs | 2 +- .../binding-python/src/circuit/instruction.rs | 4 +- .../binding-python/src/circuit/operation.rs | 4 +- .../binding-python/src/circuit/parameter.rs | 2 +- .../src/circuit/symbolic_matrix.rs | 4 +- .../src/compile/commutation/checker.rs | 18 +++++-- crates/binding-python/src/compile/compiler.rs | 18 +++++-- .../src/compile/knowledge/library.rs | 16 ++++-- .../src/compile/knowledge/matcher.rs | 6 ++- .../src/compile/knowledge/rule.rs | 12 +++-- .../src/compile/resource/manager.rs | 6 ++- .../src/compile/resource/model.rs | 24 +++++++-- .../src/compile/resource/policy.rs | 12 ++++- .../src/compile/sabre/routing.rs | 26 ++++++++-- .../src/compile/transform/canonicalize.rs | 18 +++++-- .../src/compile/transform/decompose/config.rs | 9 ++-- .../transform/decompose/mc_gate/mc_su2.rs | 3 +- .../compile/transform/decompose/unitary.rs | 9 ++-- .../src/compile/transform/layout.rs | 36 ++++++++++--- .../src/compile/transform/result.rs | 9 +++- .../src/compile/transform/rewrite.rs | 30 +++++++++-- .../src/compile/transform/routing.rs | 12 ++++- .../src/compile/transform/routing_basis.rs | 6 ++- .../binding-python/src/device/device_impl.rs | 8 +-- crates/binding-python/src/device/layout.rs | 2 +- crates/binding-python/src/device/noise.rs | 10 ++-- crates/binding-python/src/device/qubit.rs | 4 +- crates/binding-python/src/device/result.rs | 6 +-- crates/binding-python/src/device/topology.rs | 2 +- .../src/error_mitigation/mod.rs | 52 +++++++++++++++---- crates/binding-python/src/qis/evolution.rs | 2 +- crates/binding-python/src/qis/hamiltonian.rs | 2 +- crates/binding-python/src/qis/pauli.rs | 6 +-- .../binding-python/src/qis/state/classical.rs | 8 ++- .../src/qis/state/density_matrix.rs | 6 ++- .../src/qis/state/density_matrix_noise.rs | 6 ++- .../src/qis/state/stabilizer.rs | 12 ++++- .../src/qis/state/statevector.rs | 2 +- 52 files changed, 401 insertions(+), 132 deletions(-) diff --git a/crates/binding-python/src/circuit/ansatz/feature_map.rs b/crates/binding-python/src/circuit/ansatz/feature_map.rs index be26222..1d99422 100644 --- a/crates/binding-python/src/circuit/ansatz/feature_map.rs +++ b/crates/binding-python/src/circuit/ansatz/feature_map.rs @@ -37,7 +37,11 @@ use super::two_local::PyEntanglementTopology; /// >>> circuit = ae.build_circuit("x") /// >>> ae.num_parameters() /// 4 -#[pyclass(name = "AngleEncoding", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "AngleEncoding", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyAngleEncoding { pub(crate) inner: AngleEncoding, @@ -133,7 +137,11 @@ impl PyAngleEncoding { } /// Computational basis encoding from a bitstring. -#[pyclass(name = "BasisEncoding", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "BasisEncoding", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyBasisEncoding { pub(crate) inner: BasisEncoding, @@ -204,7 +212,11 @@ impl PyBasisEncoding { } /// A first-order Z feature map without entanglement. -#[pyclass(name = "ZFeatureMap", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "ZFeatureMap", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyZFeatureMap { pub(crate) inner: ZFeatureMap, @@ -286,7 +298,11 @@ impl PyZFeatureMap { } /// An IQP-style diagonal feature map. -#[pyclass(name = "IQPFeatureMap", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "IQPFeatureMap", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyIQPFeatureMap { pub(crate) inner: IQPFeatureMap, @@ -392,7 +408,11 @@ impl PyIQPFeatureMap { /// >>> circuit = fm.build_circuit("x") /// >>> fm.num_parameters() /// 3 -#[pyclass(name = "ZZFeatureMap", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "ZZFeatureMap", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyZZFeatureMap { pub(crate) inner: ZZFeatureMap, @@ -532,7 +552,11 @@ impl PyZZFeatureMap { /// ... .paulis([PauliString.from_str("Z"), PauliString.from_str("ZZ")]) /// ... .entanglement(EntanglementTopology.full())) /// >>> circuit = fm.build_circuit("x") -#[pyclass(name = "PauliFeatureMap", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "PauliFeatureMap", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyPauliFeatureMap { pub(crate) inner: PauliFeatureMap, diff --git a/crates/binding-python/src/circuit/ansatz/hamiltonian_evolution.rs b/crates/binding-python/src/circuit/ansatz/hamiltonian_evolution.rs index 7280c4c..3228bf2 100644 --- a/crates/binding-python/src/circuit/ansatz/hamiltonian_evolution.rs +++ b/crates/binding-python/src/circuit/ansatz/hamiltonian_evolution.rs @@ -42,7 +42,11 @@ use crate::qis::hamiltonian::PyHamiltonian; /// >>> s1 = EvolutionStrategy.exact() /// >>> s2 = EvolutionStrategy.auto(steps=10) /// >>> s3 = EvolutionStrategy.trotter(TrotterMode.second_order(), steps=5) -#[pyclass(name = "EvolutionStrategy", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "EvolutionStrategy", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyEvolutionStrategy { pub(crate) inner: EvolutionStrategy, @@ -236,7 +240,12 @@ impl PyEvolutionStrategy { /// ... print("Mathematically exact decomposition") /// ... else: /// ... print(f"Trotter with {info.steps} steps, mode={info.trotter_mode}") -#[pyclass(name = "EvolutionInfo", module = "cqlib.circuit.ansatz", get_all)] +#[pyclass( + name = "EvolutionInfo", + module = "cqlib.circuit.ansatz", + get_all, + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyEvolutionInfo { /// ``True`` iff the decomposition is mathematically exact. @@ -349,7 +358,11 @@ impl PyEvolutionInfo { /// >>> circuit2 = ansatz2.build_circuit("ignored") /// >>> # circuit2 has one symbolic parameter "tau" /// ``` -#[pyclass(name = "PauliEvolutionAnsatz", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "PauliEvolutionAnsatz", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyPauliEvolutionAnsatz { pub(crate) inner: PauliEvolutionAnsatz, diff --git a/crates/binding-python/src/circuit/ansatz/layers.rs b/crates/binding-python/src/circuit/ansatz/layers.rs index 6477db2..15703b2 100644 --- a/crates/binding-python/src/circuit/ansatz/layers.rs +++ b/crates/binding-python/src/circuit/ansatz/layers.rs @@ -21,7 +21,11 @@ use crate::circuit::circuit_impl::PyCircuit; use crate::circuit::gate::PyStandardGate; /// Basic entangler layers with one rotation per qubit followed by ring entanglement. -#[pyclass(name = "BasicEntanglerLayers", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "BasicEntanglerLayers", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyBasicEntanglerLayers { pub(crate) inner: BasicEntanglerLayers, @@ -121,7 +125,11 @@ impl PyBasicEntanglerLayers { /// Uses CX by default because the range pattern is directed and has explicit /// control-target semantics. Users can still choose CX, CY, or CZ manually to /// match backend-native gates or experiment-specific circuit conventions. -#[pyclass(name = "StronglyEntanglingLayers", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "StronglyEntanglingLayers", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyStronglyEntanglingLayers { pub(crate) inner: StronglyEntanglingLayers, diff --git a/crates/binding-python/src/circuit/ansatz/qaoa.rs b/crates/binding-python/src/circuit/ansatz/qaoa.rs index 59c6e2e..afe60dd 100644 --- a/crates/binding-python/src/circuit/ansatz/qaoa.rs +++ b/crates/binding-python/src/circuit/ansatz/qaoa.rs @@ -40,7 +40,11 @@ use super::hamiltonian_evolution::PyEvolutionStrategy; /// >>> circuit = ansatz.build_circuit("p") /// >>> ansatz.num_parameters() /// 6 -#[pyclass(name = "QAOAAnsatz", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "QAOAAnsatz", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyQAOAAnsatz { pub(crate) inner: QAOAAnsatz, diff --git a/crates/binding-python/src/circuit/ansatz/two_local.rs b/crates/binding-python/src/circuit/ansatz/two_local.rs index dbf21c0..efec3a0 100644 --- a/crates/binding-python/src/circuit/ansatz/two_local.rs +++ b/crates/binding-python/src/circuit/ansatz/two_local.rs @@ -30,7 +30,11 @@ use crate::circuit::gate::PyStandardGate; /// >>> t = EntanglementTopology.linear() /// >>> t = EntanglementTopology.full() /// >>> t = EntanglementTopology.custom([(0, 1), (1, 2)]) -#[pyclass(name = "EntanglementTopology", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "EntanglementTopology", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyEntanglementTopology { pub(crate) inner: EntanglementTopology, @@ -156,7 +160,11 @@ impl PyEntanglementTopology { /// >>> circuit = ansatz.build_circuit("theta") /// >>> ansatz.num_parameters() /// 18 -#[pyclass(name = "TwoLocal", module = "cqlib.circuit.ansatz")] +#[pyclass( + name = "TwoLocal", + module = "cqlib.circuit.ansatz", + skip_from_py_object +)] #[derive(Clone)] pub struct PyTwoLocal { pub(crate) inner: TwoLocal, diff --git a/crates/binding-python/src/circuit/bit.rs b/crates/binding-python/src/circuit/bit.rs index 8a5b1a5..2125888 100644 --- a/crates/binding-python/src/circuit/bit.rs +++ b/crates/binding-python/src/circuit/bit.rs @@ -83,7 +83,7 @@ use std::hash::{Hash, Hasher}; /// - Qubits with the same index are considered equal. /// - Qubits can be used as dictionary keys (hashable). /// - Comparison operators (`<`, `<=`, `>`, `>=`) compare by index. -#[pyclass(name = "Qubit", module = "cqlib.circuit")] +#[pyclass(name = "Qubit", module = "cqlib.circuit", from_py_object)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PyQubit { /// The underlying core `Qubit` type. diff --git a/crates/binding-python/src/circuit/circuit_impl.rs b/crates/binding-python/src/circuit/circuit_impl.rs index a413143..884e72e 100644 --- a/crates/binding-python/src/circuit/circuit_impl.rs +++ b/crates/binding-python/src/circuit/circuit_impl.rs @@ -60,7 +60,7 @@ impl PyParamLike { } /// Mutable quantum circuit with gate, parameter, and dynamic-control support. -#[pyclass(name = "Circuit", module = "cqlib.circuit")] +#[pyclass(name = "Circuit", module = "cqlib.circuit", skip_from_py_object)] #[derive(Debug, Clone)] pub struct PyCircuit { pub(crate) inner: Circuit, diff --git a/crates/binding-python/src/circuit/classical.rs b/crates/binding-python/src/circuit/classical.rs index 61b49c1..2491a7e 100644 --- a/crates/binding-python/src/circuit/classical.rs +++ b/crates/binding-python/src/circuit/classical.rs @@ -36,7 +36,7 @@ fn hash_value(value: impl Hash) -> u64 { } /// Process-local identity shared by classical handles owned by one circuit. -#[pyclass(name = "CircuitId", module = "cqlib.circuit")] +#[pyclass(name = "CircuitId", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PyCircuitId { pub(crate) inner: CircuitId, @@ -86,7 +86,7 @@ impl From for PyCircuitId { } /// Static type of a runtime classical expression or storage location. -#[pyclass(name = "ClassicalType", module = "cqlib.circuit")] +#[pyclass(name = "ClassicalType", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PyClassicalType { pub(crate) inner: ClassicalType, @@ -191,7 +191,7 @@ impl From for PyClassicalType { } /// Circuit-local handle to mutable runtime classical storage. -#[pyclass(name = "ClassicalVar", module = "cqlib.circuit")] +#[pyclass(name = "ClassicalVar", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PyClassicalVar { pub(crate) inner: ClassicalVar, @@ -269,7 +269,7 @@ impl From for PyClassicalVar { } /// Circuit-local handle to an immutable runtime classical value. -#[pyclass(name = "ClassicalValue", module = "cqlib.circuit")] +#[pyclass(name = "ClassicalValue", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PyClassicalValue { pub(crate) inner: ClassicalValue, @@ -340,7 +340,7 @@ impl From for PyClassicalValue { } /// Measurement receipt containing its immutable result and measured qubit order. -#[pyclass(name = "Measurement", module = "cqlib.circuit")] +#[pyclass(name = "Measurement", module = "cqlib.circuit", skip_from_py_object)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct PyMeasurement { pub(crate) inner: Measurement, diff --git a/crates/binding-python/src/circuit/classical_expr.rs b/crates/binding-python/src/circuit/classical_expr.rs index 110d7e8..7815a13 100644 --- a/crates/binding-python/src/circuit/classical_expr.rs +++ b/crates/binding-python/src/circuit/classical_expr.rs @@ -24,7 +24,7 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; /// Typed classical expression used by dynamic-circuit control flow. -#[pyclass(name = "ClassicalExpr", module = "cqlib.circuit")] +#[pyclass(name = "ClassicalExpr", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PyClassicalExpr { pub(crate) inner: ClassicalExpr, diff --git a/crates/binding-python/src/circuit/control_flow.rs b/crates/binding-python/src/circuit/control_flow.rs index af16bf7..91072e3 100644 --- a/crates/binding-python/src/circuit/control_flow.rs +++ b/crates/binding-python/src/circuit/control_flow.rs @@ -29,7 +29,7 @@ use cqlib_core::circuit::{ use pyo3::prelude::*; /// Ordered construction-time operations owned by one control-flow region. -#[pyclass(name = "ValueControlBody", module = "cqlib.circuit")] +#[pyclass(name = "ValueControlBody", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone)] pub struct PyValueControlBody { pub(crate) inner: ValueControlBody, @@ -91,7 +91,7 @@ impl PyValueControlBody { } /// Exact integer match and body used by a construction-time switch. -#[pyclass(name = "ValueSwitchCase", module = "cqlib.circuit")] +#[pyclass(name = "ValueSwitchCase", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone)] pub struct PyValueSwitchCase { pub(crate) inner: ValueSwitchCase, @@ -139,14 +139,14 @@ impl PyValueSwitchCase { /// /// This wraps `ValueClassicalControlOp`; it is not a quantum gate and has no /// unitary matrix representation. -#[pyclass(name = "ClassicalControlOp", module = "cqlib.circuit")] +#[pyclass(name = "ClassicalControlOp", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone)] pub struct PyClassicalControlOp { pub(crate) inner: ValueClassicalControlOp, } /// Temporary callback builder used by `Circuit.switch`. -#[pyclass(name = "_SwitchBuilder", module = "cqlib.circuit")] +#[pyclass(name = "_SwitchBuilder", module = "cqlib.circuit", skip_from_py_object)] pub struct PySwitchBuilder { pub(crate) circuit: Py, pub(crate) transaction: Option, diff --git a/crates/binding-python/src/circuit/gate/circuit_gate.rs b/crates/binding-python/src/circuit/gate/circuit_gate.rs index c25785c..555a8fa 100644 --- a/crates/binding-python/src/circuit/gate/circuit_gate.rs +++ b/crates/binding-python/src/circuit/gate/circuit_gate.rs @@ -25,7 +25,7 @@ use cqlib_core::circuit::gate::{CircuitGate, FrozenCircuit}; use pyo3::prelude::*; /// Immutable circuit definition suitable for use inside a gate. -#[pyclass(name = "FrozenCircuit", module = "cqlib.circuit.gates")] +#[pyclass(name = "FrozenCircuit", module = "cqlib.circuit.gates", from_py_object)] #[derive(Clone, Debug)] pub struct PyFrozenCircuit { pub(crate) inner: FrozenCircuit, @@ -109,7 +109,7 @@ impl PyFrozenCircuit { } /// Composite gate defined by an immutable circuit. -#[pyclass(name = "CircuitGate", module = "cqlib.circuit.gates")] +#[pyclass(name = "CircuitGate", module = "cqlib.circuit.gates", from_py_object)] #[derive(Clone, Debug)] pub struct PyCircuitGate { pub(crate) inner: CircuitGate, diff --git a/crates/binding-python/src/circuit/gate/directive.rs b/crates/binding-python/src/circuit/gate/directive.rs index 26f2fcd..8d1370c 100644 --- a/crates/binding-python/src/circuit/gate/directive.rs +++ b/crates/binding-python/src/circuit/gate/directive.rs @@ -19,7 +19,7 @@ use cqlib_core::circuit::gate::directive::Directive; use pyo3::prelude::*; /// Non-unitary barrier, measurement, or reset instruction. -#[pyclass(name = "Directive", module = "cqlib.circuit.gates")] +#[pyclass(name = "Directive", module = "cqlib.circuit.gates", from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PyDirective { pub(crate) inner: Directive, diff --git a/crates/binding-python/src/circuit/gate/mc_gate.rs b/crates/binding-python/src/circuit/gate/mc_gate.rs index 5a9a2b8..4b0b838 100644 --- a/crates/binding-python/src/circuit/gate/mc_gate.rs +++ b/crates/binding-python/src/circuit/gate/mc_gate.rs @@ -28,7 +28,7 @@ use pyo3::{PyResult, pyclass, pymethods}; use std::fmt; /// Multi-controlled standard gate with optional bound parameters. -#[pyclass(name = "MCGate", module = "cqlib.circuit.gates")] +#[pyclass(name = "MCGate", module = "cqlib.circuit.gates", from_py_object)] #[derive(Debug, Clone)] pub struct PyMcGate { pub(crate) inner: MCGate, diff --git a/crates/binding-python/src/circuit/gate/standard.rs b/crates/binding-python/src/circuit/gate/standard.rs index 1541f81..4afc92d 100644 --- a/crates/binding-python/src/circuit/gate/standard.rs +++ b/crates/binding-python/src/circuit/gate/standard.rs @@ -33,7 +33,7 @@ use std::hash::{Hash, Hasher}; use std::sync::RwLock; /// Native standard gate with optional bound symbolic parameters. -#[pyclass(name = "StandardGate", module = "cqlib.circuit.gates")] +#[pyclass(name = "StandardGate", module = "cqlib.circuit.gates", from_py_object)] #[derive(Debug)] pub struct PyStandardGate { pub inner: StandardGate, diff --git a/crates/binding-python/src/circuit/gate/unitary.rs b/crates/binding-python/src/circuit/gate/unitary.rs index baaf96f..0bda4af 100644 --- a/crates/binding-python/src/circuit/gate/unitary.rs +++ b/crates/binding-python/src/circuit/gate/unitary.rs @@ -27,7 +27,7 @@ use pyo3::{PyResult, Python, pyclass, pymethods}; use std::sync::Arc; /// User-defined unitary gate with stable definition identity. -#[pyclass(name = "UnitaryGate", module = "cqlib.circuit.gates")] +#[pyclass(name = "UnitaryGate", module = "cqlib.circuit.gates", from_py_object)] #[derive(Debug, Clone)] pub struct PyUnitaryGate { inner: UnitaryGate, diff --git a/crates/binding-python/src/circuit/instruction.rs b/crates/binding-python/src/circuit/instruction.rs index 9320d1a..868347b 100644 --- a/crates/binding-python/src/circuit/instruction.rs +++ b/crates/binding-python/src/circuit/instruction.rs @@ -24,7 +24,7 @@ use cqlib_core::circuit::{Instruction, ValueInstruction}; use pyo3::prelude::*; /// Python wrapper around the core storage-IR instruction enum. -#[pyclass(name = "Instruction", module = "cqlib.circuit")] +#[pyclass(name = "Instruction", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone)] pub struct PyInstruction { pub(crate) inner: Instruction, @@ -190,7 +190,7 @@ impl PyInstruction { } /// Python wrapper around the self-contained construction-IR instruction enum. -#[pyclass(name = "ValueInstruction", module = "cqlib.circuit")] +#[pyclass(name = "ValueInstruction", module = "cqlib.circuit", from_py_object)] #[derive(Debug, Clone)] pub struct PyValueInstruction { pub(crate) inner: ValueInstruction, diff --git a/crates/binding-python/src/circuit/operation.rs b/crates/binding-python/src/circuit/operation.rs index ed94528..50eb2c0 100644 --- a/crates/binding-python/src/circuit/operation.rs +++ b/crates/binding-python/src/circuit/operation.rs @@ -65,7 +65,7 @@ pub(crate) fn extract_parameter_value(value: &Bound<'_, PyAny>) -> PyResult( } /// Optimization effort selected for the compiler workflow. -#[pyclass(name = "CompileMode", module = "cqlib.compile")] +#[pyclass(name = "CompileMode", module = "cqlib.compile", from_py_object)] #[derive(Clone, Copy, Debug)] pub struct PyCompileMode { pub(crate) inner: CompileMode, @@ -204,7 +204,7 @@ impl PyCompileMode { } /// Immutable compiler workflow configuration snapshot. -#[pyclass(name = "CompileConfig", module = "cqlib.compile")] +#[pyclass(name = "CompileConfig", module = "cqlib.compile", from_py_object)] #[derive(Clone, Debug)] pub struct PyCompileConfig { pub(crate) inner: CompileConfig, @@ -346,7 +346,11 @@ impl PyCompileConfig { } /// Per-step execution record produced by a compiler workflow run. -#[pyclass(name = "WorkflowStepReport", module = "cqlib.compile")] +#[pyclass( + name = "WorkflowStepReport", + module = "cqlib.compile", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyWorkflowStepReport { pub(crate) inner: WorkflowStepReport, @@ -408,7 +412,7 @@ impl PyWorkflowStepReport { } /// Result returned by `cqlib.compile.compile`. -#[pyclass(name = "CompileResult", module = "cqlib.compile")] +#[pyclass(name = "CompileResult", module = "cqlib.compile", skip_from_py_object)] #[derive(Clone, Debug)] pub struct PyCompileResult { pub(crate) inner: CompileResult, @@ -470,7 +474,11 @@ impl PyCompileResult { } /// Reusable compiler optimization workflow. -#[pyclass(name = "CompilerWorkflow", module = "cqlib.compile")] +#[pyclass( + name = "CompilerWorkflow", + module = "cqlib.compile", + skip_from_py_object +)] pub struct PyCompilerWorkflow { inner: CompilerWorkflow, } diff --git a/crates/binding-python/src/compile/knowledge/library.rs b/crates/binding-python/src/compile/knowledge/library.rs index 6916876..b92503e 100644 --- a/crates/binding-python/src/compile/knowledge/library.rs +++ b/crates/binding-python/src/compile/knowledge/library.rs @@ -44,7 +44,7 @@ fn load_error(error: LoadError) -> PyErr { } /// Stable library-local identifier for a knowledge rule. -#[pyclass(name = "RuleId", module = "cqlib.compile.knowledge")] +#[pyclass(name = "RuleId", module = "cqlib.compile.knowledge", from_py_object)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PyRuleId { pub(crate) inner: RuleId, @@ -91,7 +91,7 @@ impl PyRuleId { } /// Coarse compiler use-case assigned to a knowledge rule. -#[pyclass(name = "RuleKind", module = "cqlib.compile.knowledge")] +#[pyclass(name = "RuleKind", module = "cqlib.compile.knowledge", from_py_object)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PyRuleKind { pub(crate) inner: RuleKind, @@ -209,7 +209,11 @@ impl PyRuleKind { } /// Precomputed selection metadata for a rule in a library. -#[pyclass(name = "RuleMetadata", module = "cqlib.compile.knowledge")] +#[pyclass( + name = "RuleMetadata", + module = "cqlib.compile.knowledge", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyRuleMetadata { inner: RuleMetadata, @@ -286,7 +290,11 @@ impl PyRuleMetadata { } /// Validated knowledge-rule collection with compiler selection indexes. -#[pyclass(name = "RuleLibrary", module = "cqlib.compile.knowledge")] +#[pyclass( + name = "RuleLibrary", + module = "cqlib.compile.knowledge", + skip_from_py_object +)] #[derive(Clone, Debug, Default)] pub struct PyRuleLibrary { inner: RuleLibrary, diff --git a/crates/binding-python/src/compile/knowledge/matcher.rs b/crates/binding-python/src/compile/knowledge/matcher.rs index 876b755..6d2a63f 100644 --- a/crates/binding-python/src/compile/knowledge/matcher.rs +++ b/crates/binding-python/src/compile/knowledge/matcher.rs @@ -32,7 +32,11 @@ fn operation_parts( } /// Symbolic parameter and qubit bindings produced by a structural rule match. -#[pyclass(name = "MatchBindings", module = "cqlib.compile.knowledge")] +#[pyclass( + name = "MatchBindings", + module = "cqlib.compile.knowledge", + skip_from_py_object +)] #[derive(Clone, Debug, Default)] pub struct PyMatchBindings { pub(crate) inner: MatchBindings, diff --git a/crates/binding-python/src/compile/knowledge/rule.rs b/crates/binding-python/src/compile/knowledge/rule.rs index fd9908d..5d62a86 100644 --- a/crates/binding-python/src/compile/knowledge/rule.rs +++ b/crates/binding-python/src/compile/knowledge/rule.rs @@ -24,7 +24,7 @@ fn parameter_values(values: &[ParameterValue]) -> Vec { } /// One gate-like operation in a knowledge rule's match or rewrite block. -#[pyclass(name = "RuleItem", module = "cqlib.compile.knowledge")] +#[pyclass(name = "RuleItem", module = "cqlib.compile.knowledge", from_py_object)] #[derive(Clone, Debug)] pub struct PyRuleItem { pub(crate) inner: RuleItem, @@ -115,7 +115,7 @@ impl PyRuleItem { } /// A symbolic condition required for a knowledge rule to match. -#[pyclass(name = "Condition", module = "cqlib.compile.knowledge")] +#[pyclass(name = "Condition", module = "cqlib.compile.knowledge", from_py_object)] #[derive(Clone, Debug)] pub struct PyCondition { pub(crate) inner: Condition, @@ -198,7 +198,11 @@ impl PyCondition { } /// Diagnostic value returned by knowledge-rule equivalence verification. -#[pyclass(name = "VerifyResult", module = "cqlib.compile.knowledge")] +#[pyclass( + name = "VerifyResult", + module = "cqlib.compile.knowledge", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyVerifyResult { status: &'static str, @@ -272,7 +276,7 @@ impl PyVerifyResult { } /// A validated compiler knowledge rewrite rule. -#[pyclass(name = "Rule", module = "cqlib.compile.knowledge")] +#[pyclass(name = "Rule", module = "cqlib.compile.knowledge", from_py_object)] #[derive(Clone, Debug)] pub struct PyRule { pub(crate) inner: Rule, diff --git a/crates/binding-python/src/compile/resource/manager.rs b/crates/binding-python/src/compile/resource/manager.rs index 856e5ae..9442cd1 100644 --- a/crates/binding-python/src/compile/resource/manager.rs +++ b/crates/binding-python/src/compile/resource/manager.rs @@ -19,7 +19,11 @@ use cqlib_core::compile::resource::{ResourceLimits, ResourceManager, ResourcePol use pyo3::prelude::*; /// Python wrapper around compiler-visible ancillary-resource bookkeeping. -#[pyclass(name = "ResourceManager", module = "cqlib.compile.resource")] +#[pyclass( + name = "ResourceManager", + module = "cqlib.compile.resource", + skip_from_py_object +)] #[derive(Debug)] pub struct PyResourceManager { pub(crate) inner: ResourceManager, diff --git a/crates/binding-python/src/compile/resource/model.rs b/crates/binding-python/src/compile/resource/model.rs index 45f2339..aa3d8e1 100644 --- a/crates/binding-python/src/compile/resource/model.rs +++ b/crates/binding-python/src/compile/resource/model.rs @@ -22,7 +22,11 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; /// State-restoration contract for ancillary qubits. -#[pyclass(name = "AncillaRequirement", module = "cqlib.compile.resource")] +#[pyclass( + name = "AncillaRequirement", + module = "cqlib.compile.resource", + from_py_object +)] #[derive(Clone, Copy, Debug)] pub struct PyAncillaRequirement { pub(crate) inner: AncillaRequirement, @@ -90,7 +94,11 @@ impl PyAncillaRequirement { } /// Python value object describing an ancillary-resource request. -#[pyclass(name = "ResourceRequest", module = "cqlib.compile.resource")] +#[pyclass( + name = "ResourceRequest", + module = "cqlib.compile.resource", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyResourceRequest { pub(crate) inner: ResourceRequest, @@ -181,7 +189,11 @@ impl PyResourceRequest { } /// Side-effect-free, manager-specific allocation preview. -#[pyclass(name = "ResourcePlan", module = "cqlib.compile.resource")] +#[pyclass( + name = "ResourcePlan", + module = "cqlib.compile.resource", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyResourcePlan { pub(crate) inner: ResourcePlan, @@ -244,7 +256,11 @@ impl PyResourcePlan { } /// Credential for an active ancillary-resource lease. -#[pyclass(name = "ResourceLease", module = "cqlib.compile.resource")] +#[pyclass( + name = "ResourceLease", + module = "cqlib.compile.resource", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyResourceLease { pub(crate) inner: ResourceLease, diff --git a/crates/binding-python/src/compile/resource/policy.rs b/crates/binding-python/src/compile/resource/policy.rs index 0dbf7c2..e198111 100644 --- a/crates/binding-python/src/compile/resource/policy.rs +++ b/crates/binding-python/src/compile/resource/policy.rs @@ -14,7 +14,11 @@ use cqlib_core::compile::resource::{ResourceLimits, ResourcePolicy}; use pyo3::prelude::*; /// Python wrapper for ancillary-resource permissions. -#[pyclass(name = "ResourcePolicy", module = "cqlib.compile.resource")] +#[pyclass( + name = "ResourcePolicy", + module = "cqlib.compile.resource", + from_py_object +)] #[derive(Clone, Copy, Debug)] pub struct PyResourcePolicy { pub(crate) inner: ResourcePolicy, @@ -81,7 +85,11 @@ impl PyResourcePolicy { } /// Python wrapper for hard target-derived resource limits. -#[pyclass(name = "ResourceLimits", module = "cqlib.compile.resource")] +#[pyclass( + name = "ResourceLimits", + module = "cqlib.compile.resource", + from_py_object +)] #[derive(Clone, Copy, Debug)] pub struct PyResourceLimits { pub(crate) inner: ResourceLimits, diff --git a/crates/binding-python/src/compile/sabre/routing.rs b/crates/binding-python/src/compile/sabre/routing.rs index 8a93518..49f5704 100644 --- a/crates/binding-python/src/compile/sabre/routing.rs +++ b/crates/binding-python/src/compile/sabre/routing.rs @@ -23,7 +23,11 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; /// Objective used to select the best result among independent SABRE trials. -#[pyclass(name = "SabreTrialObjective", module = "cqlib.compile.sabre")] +#[pyclass( + name = "SabreTrialObjective", + module = "cqlib.compile.sabre", + from_py_object +)] #[derive(Clone, Copy, Debug)] pub struct PySabreTrialObjective { inner: SabreTrialObjective, @@ -105,7 +109,11 @@ impl PySabreTrialObjective { } /// Swap-selection weights and fallback limits used by SABRE. -#[pyclass(name = "SabreHeuristicConfig", module = "cqlib.compile.sabre")] +#[pyclass( + name = "SabreHeuristicConfig", + module = "cqlib.compile.sabre", + from_py_object +)] #[derive(Clone, Debug)] pub struct PySabreHeuristicConfig { inner: SabreHeuristicConfig, @@ -198,7 +206,7 @@ impl PySabreHeuristicConfig { } /// Configuration shared by SABRE layout refinement and routing. -#[pyclass(name = "SabreConfig", module = "cqlib.compile.sabre")] +#[pyclass(name = "SabreConfig", module = "cqlib.compile.sabre", from_py_object)] #[derive(Clone, Debug)] pub struct PySabreConfig { pub(crate) inner: SabreConfig, @@ -309,7 +317,11 @@ impl PySabreConfig { } /// Diagnostics emitted by a completed SABRE routing run. -#[pyclass(name = "SabreRoutingDiagnostics", module = "cqlib.compile.sabre")] +#[pyclass( + name = "SabreRoutingDiagnostics", + module = "cqlib.compile.sabre", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PySabreRoutingDiagnostics { inner: SabreRoutingDiagnostics, @@ -379,7 +391,11 @@ impl PySabreRoutingDiagnostics { } /// Routed circuit, selected layouts, and routing diagnostics. -#[pyclass(name = "SabreRoutingResult", module = "cqlib.compile.sabre")] +#[pyclass( + name = "SabreRoutingResult", + module = "cqlib.compile.sabre", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PySabreRoutingResult { inner: SabreRoutingResult, diff --git a/crates/binding-python/src/compile/transform/canonicalize.rs b/crates/binding-python/src/compile/transform/canonicalize.rs index d42b1d8..bd0e072 100644 --- a/crates/binding-python/src/compile/transform/canonicalize.rs +++ b/crates/binding-python/src/compile/transform/canonicalize.rs @@ -18,7 +18,11 @@ use cqlib_core::compile::transform::{ use pyo3::prelude::*; /// Configuration for circuit canonicalization. -#[pyclass(name = "CanonicalizeConfig", module = "cqlib.compile.transform")] +#[pyclass( + name = "CanonicalizeConfig", + module = "cqlib.compile.transform", + from_py_object +)] #[derive(Clone, Debug)] pub struct PyCanonicalizeConfig { pub(crate) inner: CanonicalizeConfig, @@ -127,7 +131,11 @@ impl PyCanonicalizeConfig { } /// Result of a canonicalization run. -#[pyclass(name = "CanonicalizeResult", module = "cqlib.compile.transform")] +#[pyclass( + name = "CanonicalizeResult", + module = "cqlib.compile.transform", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyCanonicalizeResult { inner: CanonicalizeResult, @@ -178,7 +186,11 @@ impl PyCanonicalizeResult { } /// Configurable circuit canonicalizer. -#[pyclass(name = "Canonicalizer", module = "cqlib.compile.transform")] +#[pyclass( + name = "Canonicalizer", + module = "cqlib.compile.transform", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyCanonicalizer { inner: Canonicalizer, diff --git a/crates/binding-python/src/compile/transform/decompose/config.rs b/crates/binding-python/src/compile/transform/decompose/config.rs index e21d4d1..856250c 100644 --- a/crates/binding-python/src/compile/transform/decompose/config.rs +++ b/crates/binding-python/src/compile/transform/decompose/config.rs @@ -20,7 +20,8 @@ use std::hash::{Hash, Hasher}; /// Output interaction basis used for numeric two-qubit unitary synthesis. #[pyclass( name = "TwoQubitUnitaryDecomposeBasis", - module = "cqlib.compile.transform.decompose" + module = "cqlib.compile.transform.decompose", + from_py_object )] #[derive(Clone, Copy, Debug)] pub struct PyTwoQubitUnitaryDecomposeBasis { @@ -74,7 +75,8 @@ impl PyTwoQubitUnitaryDecomposeBasis { /// Configuration for matrix-backed unitary decomposition. #[pyclass( name = "UnitaryDecomposeConfig", - module = "cqlib.compile.transform.decompose" + module = "cqlib.compile.transform.decompose", + from_py_object )] #[derive(Clone, Copy, Debug)] pub struct PyUnitaryDecomposeConfig { @@ -140,7 +142,8 @@ impl PyUnitaryDecomposeConfig { /// Configuration for resource-aware multi-controlled-gate decomposition. #[pyclass( name = "McGateDecomposeConfig", - module = "cqlib.compile.transform.decompose" + module = "cqlib.compile.transform.decompose", + from_py_object )] #[derive(Clone, Copy, Debug)] pub struct PyMcGateDecomposeConfig { diff --git a/crates/binding-python/src/compile/transform/decompose/mc_gate/mc_su2.rs b/crates/binding-python/src/compile/transform/decompose/mc_gate/mc_su2.rs index 30d13a6..992e232 100644 --- a/crates/binding-python/src/compile/transform/decompose/mc_gate/mc_su2.rs +++ b/crates/binding-python/src/compile/transform/decompose/mc_gate/mc_su2.rs @@ -23,7 +23,8 @@ use pyo3::prelude::*; /// Axis of a multi-controlled special-unitary rotation. #[pyclass( name = "Su2RotationAxis", - module = "cqlib.compile.transform.decompose.mc_gate" + module = "cqlib.compile.transform.decompose.mc_gate", + from_py_object )] #[derive(Clone, Copy, Debug)] pub struct PySu2RotationAxis { diff --git a/crates/binding-python/src/compile/transform/decompose/unitary.rs b/crates/binding-python/src/compile/transform/decompose/unitary.rs index 6038771..419ccae 100644 --- a/crates/binding-python/src/compile/transform/decompose/unitary.rs +++ b/crates/binding-python/src/compile/transform/decompose/unitary.rs @@ -28,7 +28,8 @@ use pyo3::prelude::*; /// Numeric decomposition of a one-qubit unitary matrix. #[pyclass( name = "OneQubitUnitaryDecomposition", - module = "cqlib.compile.transform.decompose.unitary" + module = "cqlib.compile.transform.decompose.unitary", + skip_from_py_object )] #[derive(Clone, Copy, Debug)] pub struct PyOneQubitUnitaryDecomposition { @@ -80,7 +81,8 @@ impl PyOneQubitUnitaryDecomposition { /// Standard-gate synthesis of a two-qubit unitary matrix. #[pyclass( name = "TwoQubitUnitarySynthesisResult", - module = "cqlib.compile.transform.decompose.unitary" + module = "cqlib.compile.transform.decompose.unitary", + skip_from_py_object )] #[derive(Clone, Debug)] pub struct PyTwoQubitUnitarySynthesisResult { @@ -124,7 +126,8 @@ impl PyTwoQubitUnitarySynthesisResult { /// Canonical two-qubit KAK decomposition. #[pyclass( name = "KakDecomposition", - module = "cqlib.compile.transform.decompose.unitary" + module = "cqlib.compile.transform.decompose.unitary", + skip_from_py_object )] #[derive(Clone, Debug)] pub struct PyKakDecomposition { diff --git a/crates/binding-python/src/compile/transform/layout.rs b/crates/binding-python/src/compile/transform/layout.rs index 54f553f..d8afeaf 100644 --- a/crates/binding-python/src/compile/transform/layout.rs +++ b/crates/binding-python/src/compile/transform/layout.rs @@ -119,7 +119,11 @@ fn py_sabre_layout( } /// Weighted objective used to rank candidate initial layouts. -#[pyclass(name = "LayoutObjective", module = "cqlib.compile.transform.layout")] +#[pyclass( + name = "LayoutObjective", + module = "cqlib.compile.transform.layout", + from_py_object +)] #[derive(Clone, Debug)] pub struct PyLayoutObjective { pub(crate) inner: LayoutObjective, @@ -230,7 +234,11 @@ impl PyLayoutObjective { } /// Breakdown of a layout objective score. -#[pyclass(name = "LayoutScore", module = "cqlib.compile.transform.layout")] +#[pyclass( + name = "LayoutScore", + module = "cqlib.compile.transform.layout", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyLayoutScore { inner: LayoutScore, @@ -300,7 +308,11 @@ impl PyLayoutScore { } /// Diagnostics emitted by an initial-layout algorithm. -#[pyclass(name = "LayoutDiagnostics", module = "cqlib.compile.transform.layout")] +#[pyclass( + name = "LayoutDiagnostics", + module = "cqlib.compile.transform.layout", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyLayoutDiagnostics { inner: LayoutDiagnostics, @@ -358,7 +370,11 @@ impl PyLayoutDiagnostics { } /// Selected initial layout, score, and diagnostics. -#[pyclass(name = "LayoutResult", module = "cqlib.compile.transform.layout")] +#[pyclass( + name = "LayoutResult", + module = "cqlib.compile.transform.layout", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyLayoutResult { inner: LayoutResult, @@ -408,7 +424,11 @@ impl PyLayoutResult { } /// Selects which logical interactions are hard topology constraints for VF2. -#[pyclass(name = "Vf2EdgeRequirement", module = "cqlib.compile.transform.layout")] +#[pyclass( + name = "Vf2EdgeRequirement", + module = "cqlib.compile.transform.layout", + from_py_object +)] #[derive(Clone, Copy, Debug)] pub struct PyVf2EdgeRequirement { pub(crate) inner: Vf2EdgeRequirement, @@ -472,7 +492,11 @@ impl PyVf2EdgeRequirement { } /// Configuration for VF2 perfect-layout search. -#[pyclass(name = "Vf2LayoutConfig", module = "cqlib.compile.transform.layout")] +#[pyclass( + name = "Vf2LayoutConfig", + module = "cqlib.compile.transform.layout", + from_py_object +)] #[derive(Clone, Debug)] pub struct PyVf2LayoutConfig { pub(crate) inner: Vf2LayoutConfig, diff --git a/crates/binding-python/src/compile/transform/result.rs b/crates/binding-python/src/compile/transform/result.rs index a1e0ed1..91de5da 100644 --- a/crates/binding-python/src/compile/transform/result.rs +++ b/crates/binding-python/src/compile/transform/result.rs @@ -16,7 +16,11 @@ use cqlib_core::compile::transform::decompose::DecompositionRuleStats; use pyo3::prelude::*; /// Common result returned by circuit-to-circuit transforms. -#[pyclass(name = "TransformResult", module = "cqlib.compile.transform")] +#[pyclass( + name = "TransformResult", + module = "cqlib.compile.transform", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyTransformResult { pub(crate) inner: TransformResult, @@ -63,7 +67,8 @@ impl PyTransformResult { /// Pass-local runtime decomposition-rule cache counters. #[pyclass( name = "DecompositionRuleStats", - module = "cqlib.compile.transform.decompose" + module = "cqlib.compile.transform.decompose", + skip_from_py_object )] #[derive(Clone, Copy, Debug)] pub struct PyDecompositionRuleStats { diff --git a/crates/binding-python/src/compile/transform/rewrite.rs b/crates/binding-python/src/compile/transform/rewrite.rs index a36d33e..ed543ed 100644 --- a/crates/binding-python/src/compile/transform/rewrite.rs +++ b/crates/binding-python/src/compile/transform/rewrite.rs @@ -25,7 +25,11 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; /// High-level knowledge-rule application mode. -#[pyclass(name = "RewriteMode", module = "cqlib.compile.transform")] +#[pyclass( + name = "RewriteMode", + module = "cqlib.compile.transform", + from_py_object +)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct PyRewriteMode { inner: RewriteMode, @@ -87,7 +91,11 @@ impl PyRewriteMode { } /// Configuration for knowledge-based local circuit rewrite. -#[pyclass(name = "RewriteConfig", module = "cqlib.compile.transform")] +#[pyclass( + name = "RewriteConfig", + module = "cqlib.compile.transform", + from_py_object +)] #[derive(Clone, Debug)] pub struct PyRewriteConfig { pub(crate) inner: RewriteConfig, @@ -257,7 +265,11 @@ impl PyRewriteConfig { } /// Aggregate statistics produced by one knowledge rewrite run. -#[pyclass(name = "KnowledgeRewriteStats", module = "cqlib.compile.transform")] +#[pyclass( + name = "KnowledgeRewriteStats", + module = "cqlib.compile.transform", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyKnowledgeRewriteStats { inner: KnowledgeRewriteStats, @@ -315,7 +327,11 @@ impl PyKnowledgeRewriteStats { } /// Rewritten circuit and fixed-point run metadata. -#[pyclass(name = "KnowledgeRewriteResult", module = "cqlib.compile.transform")] +#[pyclass( + name = "KnowledgeRewriteResult", + module = "cqlib.compile.transform", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyKnowledgeRewriteResult { inner: KnowledgeRewriteResult, @@ -362,7 +378,11 @@ impl PyKnowledgeRewriteResult { } /// Configurable knowledge-based local circuit rewriter. -#[pyclass(name = "KnowledgeRewriter", module = "cqlib.compile.transform")] +#[pyclass( + name = "KnowledgeRewriter", + module = "cqlib.compile.transform", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyKnowledgeRewriter { inner: KnowledgeRewriter, diff --git a/crates/binding-python/src/compile/transform/routing.rs b/crates/binding-python/src/compile/transform/routing.rs index cbd754b..634f3ab 100644 --- a/crates/binding-python/src/compile/transform/routing.rs +++ b/crates/binding-python/src/compile/transform/routing.rs @@ -85,7 +85,11 @@ fn py_route_sabre( } /// A physical circuit produced by routing, plus routing metadata. -#[pyclass(name = "RoutedCircuit", module = "cqlib.compile.transform.routing")] +#[pyclass( + name = "RoutedCircuit", + module = "cqlib.compile.transform.routing", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyRoutedCircuit { inner: RoutedCircuit, @@ -151,7 +155,11 @@ impl PyRoutedCircuit { } /// Full SABRE layout-selection and routing result. -#[pyclass(name = "SabreRouteResult", module = "cqlib.compile.transform.routing")] +#[pyclass( + name = "SabreRouteResult", + module = "cqlib.compile.transform.routing", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PySabreRouteResult { inner: SabreRouteResult, diff --git a/crates/binding-python/src/compile/transform/routing_basis.rs b/crates/binding-python/src/compile/transform/routing_basis.rs index f2117dc..a4e725f 100644 --- a/crates/binding-python/src/compile/transform/routing_basis.rs +++ b/crates/binding-python/src/compile/transform/routing_basis.rs @@ -23,7 +23,11 @@ use pyo3::prelude::*; /// /// `preferred_basis` is a hint for the 2-qubit family used when lowering CCX: /// CZ is preferred only when the basis contains CZ and does not contain CX. -#[pyclass(name = "LowerToRoutingBasis", module = "cqlib.compile.transform")] +#[pyclass( + name = "LowerToRoutingBasis", + module = "cqlib.compile.transform", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyLowerToRoutingBasis { inner: LowerToRoutingBasis, diff --git a/crates/binding-python/src/device/device_impl.rs b/crates/binding-python/src/device/device_impl.rs index 9328bb1..3416798 100644 --- a/crates/binding-python/src/device/device_impl.rs +++ b/crates/binding-python/src/device/device_impl.rs @@ -70,7 +70,7 @@ use time::OffsetDateTime; /// # Optionally set gate duration in nanoseconds /// prop.length = 35.0 # 35 ns /// ``` -#[pyclass(name = "InstructionProp", module = "cqlib.device")] +#[pyclass(name = "InstructionProp", module = "cqlib.device", from_py_object)] #[derive(Clone, Debug)] pub struct PyInstructionProp { pub(crate) inner: InstructionProp, @@ -175,7 +175,7 @@ impl PyInstructionProp { /// prop.prob_meas0_prep1 = 0.02 # P(meas 0 | prep 1) /// prop.prob_meas1_prep0 = 0.01 # P(meas 1 | prep 0) /// ``` -#[pyclass(name = "QubitProp", module = "cqlib.device")] +#[pyclass(name = "QubitProp", module = "cqlib.device", from_py_object)] #[derive(Clone, Debug)] pub struct PyQubitProp { pub(crate) inner: QubitProp, @@ -294,7 +294,7 @@ impl PyQubitProp { } } -#[pyclass(name = "EdgeProp", module = "cqlib.device")] +#[pyclass(name = "EdgeProp", module = "cqlib.device", from_py_object)] #[derive(Clone, Debug)] pub struct PyEdgeProp { pub(crate) inner: EdgeProp, @@ -386,7 +386,7 @@ impl PyEdgeProp { /// prop.t1 = 120.0 /// device.add_qubit_properties(0, prop) /// ``` -#[pyclass(name = "Device", module = "cqlib.device")] +#[pyclass(name = "Device", module = "cqlib.device", skip_from_py_object)] #[derive(Clone, Debug)] pub struct PyDevice { pub(crate) inner: Device, diff --git a/crates/binding-python/src/device/layout.rs b/crates/binding-python/src/device/layout.rs index 7e73456..1053cda 100644 --- a/crates/binding-python/src/device/layout.rs +++ b/crates/binding-python/src/device/layout.rs @@ -94,7 +94,7 @@ use std::collections::HashMap; /// # Get all mappings /// print(layout.l2p_map) # {LogicalQubit(0): PhysicalQubit(100), ...} /// ``` -#[pyclass(name = "Layout", module = "cqlib.device")] +#[pyclass(name = "Layout", module = "cqlib.device", skip_from_py_object)] #[derive(Clone, Debug)] pub struct PyLayout { pub(crate) inner: Layout, diff --git a/crates/binding-python/src/device/noise.rs b/crates/binding-python/src/device/noise.rs index f95ab46..6593d7e 100644 --- a/crates/binding-python/src/device/noise.rs +++ b/crates/binding-python/src/device/noise.rs @@ -90,7 +90,7 @@ use std::hash::{Hash, Hasher}; /// # Validate noise parameters /// assert noise.is_valid() # True if probabilities are in [0, 1] /// ``` -#[pyclass(name = "SingleQubitNoise", module = "cqlib.device")] +#[pyclass(name = "SingleQubitNoise", module = "cqlib.device", from_py_object)] #[derive(Clone, Copy, Debug, PartialEq)] pub struct PySingleQubitNoise { pub(crate) inner: SingleQubitNoise, @@ -282,7 +282,7 @@ impl PySingleQubitNoise { /// q1_noise = SingleQubitNoise.depolarizing(0.001) /// independent = TwoQubitNoise.independent(q0_noise, q1_noise) /// ``` -#[pyclass(name = "TwoQubitNoise", module = "cqlib.device")] +#[pyclass(name = "TwoQubitNoise", module = "cqlib.device", from_py_object)] #[derive(Clone, Copy, Debug, PartialEq)] pub struct PyTwoQubitNoise { pub(crate) inner: TwoQubitNoise, @@ -432,7 +432,7 @@ impl PyTwoQubitNoise { /// /// assert error.is_valid() # Both probabilities in [0, 1] /// ``` -#[pyclass(name = "ReadoutError", module = "cqlib.device")] +#[pyclass(name = "ReadoutError", module = "cqlib.device", from_py_object)] #[derive(Clone, Copy, Debug, PartialEq)] pub struct PyReadoutError { pub(crate) inner: ReadoutError, @@ -529,7 +529,7 @@ impl PyReadoutError { /// print(key.gate) # StandardGate.H /// print(key.qubits) # [0] /// ``` -#[pyclass(name = "OperationKey", module = "cqlib.device")] +#[pyclass(name = "OperationKey", module = "cqlib.device", from_py_object)] #[derive(Clone, Debug)] pub struct PyOperationKey { pub(crate) inner: OperationKey, @@ -690,7 +690,7 @@ impl PyOperationKey { /// key = OperationKey.new_single(StandardGate.H, 0) /// errors = model.get_single_qubit_errors(key) /// ``` -#[pyclass(name = "NoiseModel", module = "cqlib.device")] +#[pyclass(name = "NoiseModel", module = "cqlib.device", from_py_object)] #[derive(Clone, Debug, Default)] pub struct PyNoiseModel { pub(crate) inner: NoiseModel, diff --git a/crates/binding-python/src/device/qubit.rs b/crates/binding-python/src/device/qubit.rs index e32eca7..6f4099f 100644 --- a/crates/binding-python/src/device/qubit.rs +++ b/crates/binding-python/src/device/qubit.rs @@ -57,7 +57,7 @@ use pyo3::{Bound, PyAny, pyclass, pymethods}; /// print(lq.id) # 0 /// print(lq.qubit) # Qubit(0) /// ``` -#[pyclass(name = "LogicalQubit", module = "cqlib.device")] +#[pyclass(name = "LogicalQubit", module = "cqlib.device", skip_from_py_object)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PyLogicalQubit { pub(crate) inner: LogicalQubit, @@ -160,7 +160,7 @@ impl PyLogicalQubit { /// print(pq.id) # 100 /// print(pq.qubit) # Qubit(100) /// ``` -#[pyclass(name = "PhysicalQubit", module = "cqlib.device")] +#[pyclass(name = "PhysicalQubit", module = "cqlib.device", skip_from_py_object)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PyPhysicalQubit { pub(crate) inner: PhysicalQubit, diff --git a/crates/binding-python/src/device/result.rs b/crates/binding-python/src/device/result.rs index 83eeabe..b189b48 100644 --- a/crates/binding-python/src/device/result.rs +++ b/crates/binding-python/src/device/result.rs @@ -81,7 +81,7 @@ use std::hash::{Hash, Hasher}; /// # Convert back to string /// bitstring = outcome.to_bitstring(3) # "101" /// ``` -#[pyclass(name = "Outcome", module = "cqlib.device")] +#[pyclass(name = "Outcome", module = "cqlib.device", from_py_object)] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct PyOutcome { pub(crate) inner: Outcome, @@ -220,7 +220,7 @@ impl PyOutcome { /// if status.is_terminal(): /// print(f"Job finished with status: {status}") /// ``` -#[pyclass(name = "Status", module = "cqlib.device")] +#[pyclass(name = "Status", module = "cqlib.device", skip_from_py_object)] #[derive(Clone, Debug, PartialEq)] pub struct PyStatus { pub(crate) inner: Status, @@ -371,7 +371,7 @@ impl PyStatus { /// print(result.counts) # {"00": 512, "11": 488} /// print(result.probabilities) # {"00": 0.512, "11": 0.488} /// ``` -#[pyclass(name = "ExecutionResult", module = "cqlib.device")] +#[pyclass(name = "ExecutionResult", module = "cqlib.device", skip_from_py_object)] #[derive(Clone, Debug)] pub struct PyExecutionResult { pub(crate) inner: ExecutionResult, diff --git a/crates/binding-python/src/device/topology.rs b/crates/binding-python/src/device/topology.rs index a32f420..87b9665 100644 --- a/crates/binding-python/src/device/topology.rs +++ b/crates/binding-python/src/device/topology.rs @@ -85,7 +85,7 @@ use pyo3::{Bound, PyAny, PyResult, pyclass, pymethods}; /// topology.supports_directed_coupling(0, 1) # True (0 -> 1) /// topology.supports_directed_coupling(1, 0) # False (no 1 -> 0) /// ``` -#[pyclass(name = "Topology", module = "cqlib.device")] +#[pyclass(name = "Topology", module = "cqlib.device", from_py_object)] #[derive(Clone, Debug)] pub struct PyTopology { /// The underlying core topology. diff --git a/crates/binding-python/src/error_mitigation/mod.rs b/crates/binding-python/src/error_mitigation/mod.rs index fffcfaa..10aa78a 100644 --- a/crates/binding-python/src/error_mitigation/mod.rs +++ b/crates/binding-python/src/error_mitigation/mod.rs @@ -67,7 +67,11 @@ fn take_estimator_error(error: EstimatorErrorCell) -> PyResult<()> { } } -#[pyclass(name = "ExtrapolateMethod", module = "cqlib.error_mitigation")] +#[pyclass( + name = "ExtrapolateMethod", + module = "cqlib.error_mitigation", + from_py_object +)] #[derive(Clone, Copy, Debug)] pub struct PyExtrapolateMethod { inner: ExtrapolateMethod, @@ -127,7 +131,7 @@ impl PyExtrapolateMethod { } } -#[pyclass(name = "ZneConfig", module = "cqlib.error_mitigation")] +#[pyclass(name = "ZneConfig", module = "cqlib.error_mitigation", from_py_object)] #[derive(Clone, Debug)] pub struct PyZneConfig { inner: ZneConfig, @@ -164,7 +168,11 @@ impl PyZneConfig { } } -#[pyclass(name = "VirtualDistillationConfig", module = "cqlib.error_mitigation")] +#[pyclass( + name = "VirtualDistillationConfig", + module = "cqlib.error_mitigation", + from_py_object +)] #[derive(Clone, Debug)] pub struct PyVirtualDistillationConfig { inner: VirtualDistillationConfig, @@ -201,7 +209,11 @@ impl PyVirtualDistillationConfig { } } -#[pyclass(name = "MitigationMethod", module = "cqlib.error_mitigation")] +#[pyclass( + name = "MitigationMethod", + module = "cqlib.error_mitigation", + from_py_object +)] #[derive(Clone, Debug)] pub struct PyMitigationMethod { inner: MitigationMethod, @@ -259,7 +271,7 @@ impl PyMitigationMethod { } } -#[pyclass(name = "RunArgs", module = "cqlib.error_mitigation")] +#[pyclass(name = "RunArgs", module = "cqlib.error_mitigation", from_py_object)] #[derive(Clone, Debug)] pub struct PyRunArgs { inner: RunArgs, @@ -323,7 +335,11 @@ impl PyRunArgs { } } -#[pyclass(name = "ProcessArgs", module = "cqlib.error_mitigation")] +#[pyclass( + name = "ProcessArgs", + module = "cqlib.error_mitigation", + from_py_object +)] #[derive(Clone, Copy, Debug)] pub struct PyProcessArgs { inner: ProcessArgs, @@ -380,7 +396,11 @@ impl PyProcessArgs { } } -#[pyclass(name = "MitigatedResult", module = "cqlib.error_mitigation")] +#[pyclass( + name = "MitigatedResult", + module = "cqlib.error_mitigation", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyMitigatedResult { inner: MitigatedResult, @@ -424,7 +444,11 @@ impl PyMitigatedResult { } } -#[pyclass(name = "ZNEMitigation", module = "cqlib.error_mitigation")] +#[pyclass( + name = "ZNEMitigation", + module = "cqlib.error_mitigation", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyZNEMitigation { inner: ZNEMitigation, @@ -538,7 +562,11 @@ impl PyZNEMitigation { } } -#[pyclass(name = "VirtualDistillation", module = "cqlib.error_mitigation")] +#[pyclass( + name = "VirtualDistillation", + module = "cqlib.error_mitigation", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyVirtualDistillation { inner: VirtualDistillation, @@ -649,7 +677,11 @@ impl PyVirtualDistillation { } } -#[pyclass(name = "ErrorMitigation", module = "cqlib.error_mitigation")] +#[pyclass( + name = "ErrorMitigation", + module = "cqlib.error_mitigation", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyErrorMitigation { inner: ErrorMitigation, diff --git a/crates/binding-python/src/qis/evolution.rs b/crates/binding-python/src/qis/evolution.rs index c64afe7..d4a6433 100644 --- a/crates/binding-python/src/qis/evolution.rs +++ b/crates/binding-python/src/qis/evolution.rs @@ -32,7 +32,7 @@ use std::hash::{Hash, Hasher}; /// >>> mode1 = TrotterMode.first_order() /// >>> mode2 = TrotterMode.second_order() /// >>> mode3 = TrotterMode.randomized(42) # with seed 42 -#[pyclass(name = "TrotterMode", module = "cqlib.qis")] +#[pyclass(name = "TrotterMode", module = "cqlib.qis", skip_from_py_object)] #[derive(Clone, Debug)] pub struct PyTrotterMode { pub(crate) inner: TrotterMode, diff --git a/crates/binding-python/src/qis/hamiltonian.rs b/crates/binding-python/src/qis/hamiltonian.rs index d623b96..18ccd36 100644 --- a/crates/binding-python/src/qis/hamiltonian.rs +++ b/crates/binding-python/src/qis/hamiltonian.rs @@ -44,7 +44,7 @@ use std::fmt; /// >>> h.add_term(PauliString.from_str("XX"), 0.3) /// >>> # Simplify to merge duplicate terms /// >>> h.simplify() -#[pyclass(name = "Hamiltonian", module = "cqlib.qis")] +#[pyclass(name = "Hamiltonian", module = "cqlib.qis", from_py_object)] #[derive(Clone, Debug, PartialEq)] pub struct PyHamiltonian { pub(crate) inner: Hamiltonian, diff --git a/crates/binding-python/src/qis/pauli.rs b/crates/binding-python/src/qis/pauli.rs index f5d227e..0bc7fe4 100644 --- a/crates/binding-python/src/qis/pauli.rs +++ b/crates/binding-python/src/qis/pauli.rs @@ -35,7 +35,7 @@ use std::hash::{Hash, Hasher}; /// I (1): $i^1 = i$ /// Minus (-1): $i^2 = -1$ /// MinusI (-i): $i^3 = -i$ -#[pyclass(name = "Phase", module = "cqlib.qis")] +#[pyclass(name = "Phase", module = "cqlib.qis", skip_from_py_object)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct PyPhase { pub(crate) inner: Phase, @@ -157,7 +157,7 @@ impl PyPhase { /// Y: Pauli-Y operator /// Z: Pauli-Z (phase-flip) operator /// I: Identity operator -#[pyclass(name = "Pauli", module = "cqlib.qis")] +#[pyclass(name = "Pauli", module = "cqlib.qis", from_py_object)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PyPauli { pub(crate) inner: Pauli, @@ -262,7 +262,7 @@ impl PyPauli { /// /// A Pauli string is a tensor product of single-qubit Pauli operators across /// multiple qubits: $P = \\bigotimes_{i=0}^{N-1} P_i$ where $P_i \\in \\{I, X, Y, Z\\}$. -#[pyclass(name = "PauliString", module = "cqlib.qis")] +#[pyclass(name = "PauliString", module = "cqlib.qis", from_py_object)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct PyPauliString { pub(crate) inner: PauliString, diff --git a/crates/binding-python/src/qis/state/classical.rs b/crates/binding-python/src/qis/state/classical.rs index cba541c..c558966 100644 --- a/crates/binding-python/src/qis/state/classical.rs +++ b/crates/binding-python/src/qis/state/classical.rs @@ -19,7 +19,7 @@ use pyo3::exceptions::PyTypeError; use pyo3::prelude::*; /// A typed runtime classical value produced during circuit execution. -#[pyclass(name = "RuntimeValue", module = "cqlib.qis.state")] +#[pyclass(name = "RuntimeValue", module = "cqlib.qis.state", skip_from_py_object)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct PyRuntimeValue { pub(crate) inner: RuntimeValue, @@ -118,7 +118,11 @@ impl PyRuntimeValue { } /// Runtime classical state produced while executing a circuit. -#[pyclass(name = "ClassicalState", module = "cqlib.qis.state")] +#[pyclass( + name = "ClassicalState", + module = "cqlib.qis.state", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyClassicalState { pub(crate) inner: ClassicalState, diff --git a/crates/binding-python/src/qis/state/density_matrix.rs b/crates/binding-python/src/qis/state/density_matrix.rs index 87d9109..071bd53 100644 --- a/crates/binding-python/src/qis/state/density_matrix.rs +++ b/crates/binding-python/src/qis/state/density_matrix.rs @@ -50,7 +50,11 @@ use pyo3::types::{PyComplex, PyList}; /// probs = dm.probabilities() /// print(probs) # [0.5, 0.5] /// ``` -#[pyclass(name = "DensityMatrix", module = "cqlib.qis.state")] +#[pyclass( + name = "DensityMatrix", + module = "cqlib.qis.state", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyDensityMatrix { pub(crate) inner: DensityMatrix, diff --git a/crates/binding-python/src/qis/state/density_matrix_noise.rs b/crates/binding-python/src/qis/state/density_matrix_noise.rs index f74c545..551f778 100644 --- a/crates/binding-python/src/qis/state/density_matrix_noise.rs +++ b/crates/binding-python/src/qis/state/density_matrix_noise.rs @@ -47,7 +47,11 @@ use pyo3::prelude::*; /// # Get probabilities (P(|1>) ~ 0.99 due to 1% bit-flip noise) /// probs = sim.probabilities() /// ``` -#[pyclass(name = "DensityMatrixNoise", module = "cqlib.qis.state")] +#[pyclass( + name = "DensityMatrixNoise", + module = "cqlib.qis.state", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyDensityMatrixNoise { pub(crate) inner: DensityMatrixNoise, diff --git a/crates/binding-python/src/qis/state/stabilizer.rs b/crates/binding-python/src/qis/state/stabilizer.rs index c088a05..a2a652d 100644 --- a/crates/binding-python/src/qis/state/stabilizer.rs +++ b/crates/binding-python/src/qis/state/stabilizer.rs @@ -22,7 +22,11 @@ use cqlib_core::qis::state::stabilizer::{CircuitExecutionResult, StabilizerState use pyo3::prelude::*; /// Result of executing a Clifford circuit with a stabilizer simulator. -#[pyclass(name = "StabilizerCircuitResult", module = "cqlib.qis.state")] +#[pyclass( + name = "StabilizerCircuitResult", + module = "cqlib.qis.state", + skip_from_py_object +)] #[derive(Debug)] pub struct PyStabilizerCircuitResult { inner: CircuitExecutionResult, @@ -52,7 +56,11 @@ impl PyStabilizerCircuitResult { } /// Stabilizer state simulator for Clifford circuits. -#[pyclass(name = "StabilizerState", module = "cqlib.qis.state")] +#[pyclass( + name = "StabilizerState", + module = "cqlib.qis.state", + skip_from_py_object +)] #[derive(Clone, Debug)] pub struct PyStabilizerState { pub(crate) inner: StabilizerState, diff --git a/crates/binding-python/src/qis/state/statevector.rs b/crates/binding-python/src/qis/state/statevector.rs index 9696094..5a3f09a 100644 --- a/crates/binding-python/src/qis/state/statevector.rs +++ b/crates/binding-python/src/qis/state/statevector.rs @@ -49,7 +49,7 @@ use pyo3::types::{PyComplex, PyList}; /// probs = sv.probabilities() /// print(probs) # [0.5, 0.0, 0.0, 0.5] /// ``` -#[pyclass(name = "Statevector", module = "cqlib.qis.state")] +#[pyclass(name = "Statevector", module = "cqlib.qis.state", skip_from_py_object)] #[derive(Clone, Debug)] pub struct PyStatevector { pub(crate) inner: Statevector,