use prism_q::backend::sparse::SparseBackend;
use prism_q::gates::Gate;
use prism_q::sim::ResolvedBackend;
use prism_q::{
BackendKind, Circuit, NoiseModel, PauliObservable, PauliTerm, PrismError, run_on, sim,
};
const WIDTH: usize = usize::BITS as usize;
fn phase_chain(n: usize) -> Circuit {
let mut c = Circuit::new(n, 2);
c.add_gate(Gate::X, &[0]);
for q in 1..n {
c.add_gate(Gate::Cx, &[q - 1, q]);
}
c.add_gate(Gate::P(0.3), &[n - 1]);
c
}
#[test]
fn sparse_rejects_a_circuit_wider_than_the_basis_index() {
let circuit = phase_chain(WIDTH + 1);
let mut backend = SparseBackend::new(42);
match run_on(&mut backend, &circuit).unwrap_err() {
PrismError::IncompatibleBackend { backend, reason } => {
assert_eq!(backend, "sparse");
assert!(reason.contains("basis-index width"), "reason: {reason}");
}
other => panic!("expected an incompatible-backend rejection, got {other:?}"),
}
}
#[test]
fn sparse_runs_at_the_widest_addressable_circuit() {
let mut circuit = phase_chain(WIDTH);
circuit.add_measure(WIDTH - 1, 0);
circuit.add_measure(WIDTH - 2, 1);
let mut backend = SparseBackend::new(42);
let outcome = run_on(&mut backend, &circuit).expect("a circuit at the index width must run");
assert!(
outcome.classical_bits[0],
"the chain must carry the flip to qubit {}, the last addressable index",
WIDTH - 1
);
assert!(outcome.classical_bits[1], "and to the qubit below it");
}
#[test]
fn auto_routes_past_the_index_width_to_mps() {
let circuit = phase_chain(WIDTH + 6);
let outcome = sim::simulate(&circuit)
.backend(BackendKind::Auto)
.seed(42)
.run()
.expect("auto must serve a circuit past the sparse index width");
assert_eq!(outcome.metadata.backend, ResolvedBackend::Mps);
}
#[test]
fn noisy_auto_routes_past_the_index_width_to_mps() {
let circuit = phase_chain(WIDTH + 6);
let noise = NoiseModel::with_amplitude_damping(&circuit, 0.05);
let shots = sim::simulate(&circuit)
.backend(BackendKind::Auto)
.seed(42)
.noise(&noise)
.shots(2)
.expect("auto must serve a noisy circuit past the sparse index width");
assert_eq!(shots.metadata.backend, ResolvedBackend::Mps);
}
#[test]
fn wide_observable_terminals_reject_rather_than_shift() {
let circuit = phase_chain(WIDTH + 6);
let top = WIDTH + 5;
let values = sim::simulate(&circuit)
.backend(BackendKind::Statevector)
.seed(42)
.expectation_values(&[vec![PauliTerm::z(top)]]);
assert!(values.is_err(), "expectation_values must reject");
let observable = PauliObservable::from_terms([(1.0, vec![PauliTerm::z(top)])]).unwrap();
let grouped = sim::simulate(&circuit)
.backend(BackendKind::Statevector)
.seed(42)
.observable_expectation(&observable);
assert!(grouped.is_err(), "observable_expectation must reject");
}