burn_quantum_visualization 0.4.0

Interactive Bevy workbench for quantum circuits, states, photonic fabrics and optimization
Documentation
//! Scientific readout and import-budget regression tests, independent of the renderer.
use burn::tensor::Device;
use burn_quantum::{
    Circuit, DenseSimulator, DensityMatrix, KrausChannel, QuantumBackend, Register, Unitary,
};
use burn_quantum_visualization::{
    demo::{self, DemoKind},
    model::aggregate_probabilities,
    simulation::{self, TraceLimits},
};

fn near(a: f64, b: f64) {
    assert!((a - b).abs() < 3e-5, "{a} != {b}");
}
#[test]
fn bloch_y_signs_and_entangled_reduced_states_are_physical() {
    pollster::block_on(async {
        let device = Device::flex();
        let limits = TraceLimits::default();
        let mut c = Circuit::new(Register::qubits(1).unwrap(), 0, 0);
        c.unitary(vec![0], Unitary::h()).unwrap();
        c.unitary(vec![0], Unitary::s()).unwrap();
        c.unitary(vec![0], Unitary::z()).unwrap();
        let labels = vec!["H".into(), "S".into(), "Z".into()];
        let trace = simulation::simulate(&c, &[], &[], &labels, &device, limits)
            .await
            .unwrap();
        for (frame, expected) in
            trace
                .frames
                .iter()
                .zip([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [0., -1., 0.]])
        {
            for (a, b) in frame.subsystems[0].bloch.unwrap().into_iter().zip(expected) {
                near(a, b);
            }
            near(frame.subsystems[0].purity, 1.);
        }
        for kind in [DemoKind::Bell, DemoKind::Ghz] {
            let doc = demo::build(kind, &device, limits).await.unwrap();
            let final_state = doc.trace.frames.last().unwrap();
            near(final_state.probabilities[0], 0.5);
            near(*final_state.probabilities.last().unwrap(), 0.5);
            for sub in &final_state.subsystems {
                for a in sub.bloch.unwrap() {
                    near(a, 0.);
                }
                near(sub.purity, 0.5);
            }
            for (step, frame) in doc.trace.frames.iter().enumerate() {
                assert_eq!(frame.step, step);
                near(frame.norm, 1.);
            }
            assert_eq!(doc.trace.frames.len(), doc.circuit.operations().len() + 1);
        }
    });
}

#[test]
fn mixed_state_snapshots_do_not_invent_pure_amplitudes() {
    pollster::block_on(async {
        let device = Device::flex();
        let limits = TraceLimits::default();
        let engine = DenseSimulator::new(limits.resources());
        let mut c = Circuit::new(Register::qubits(1).unwrap(), 0, 0);
        c.unitary(vec![0], Unitary::x()).unwrap();
        let state = engine.execute(&c, None, None, 1, &device).unwrap();
        let density = DensityMatrix::from_pure(&state, engine.limits)
            .unwrap()
            .channel(
                &engine,
                &[0],
                &KrausChannel::amplitude_damping(0.25).unwrap(),
            )
            .unwrap();
        let frame = simulation::snapshot_density(&density, 0, "Damped".into(), limits)
            .await
            .unwrap();
        assert!(frame.amplitudes.is_none());
        near(frame.probabilities[0], 0.25);
        near(frame.probabilities[1], 0.75);
        near(frame.subsystems[0].bloch.unwrap()[2], -0.5);
        near(frame.subsystems[0].purity, 0.625);
    });
}

#[test]
fn qutrit_interference_and_probability_aggregation_keep_all_mass() {
    pollster::block_on(async {
        let device = Device::flex();
        let limits = TraceLimits::default();
        let doc = demo::build(DemoKind::Qutrit, &device, limits)
            .await
            .unwrap();
        let state = doc.trace.frames.last().unwrap();
        assert!(state.subsystems[0].bloch.is_none());
        for (&a, b) in state.probabilities.iter().zip([1. / 9., 4. / 9., 4. / 9.]) {
            near(a, b);
        }
        let bins = aggregate_probabilities(&state.probabilities, 2).unwrap();
        assert_eq!(bins.len(), 2);
        near(bins.iter().map(|b| b.probability).sum(), 1.);
        assert_eq!((bins[0].start, bins[0].end, bins[1].end), (0, 2, 3));
        let interference = demo::build(DemoKind::Interference, &device, limits)
            .await
            .unwrap();
        near(
            interference.trace.frames.last().unwrap().probabilities[1],
            1.,
        );
        assert!(aggregate_probabilities(&[1.], 0).is_err());
        assert!(aggregate_probabilities(&[f64::NAN], 2).is_err());
    });
}

#[test]
fn optimization_frames_are_actual_iterates_and_costs_are_not_perturbed_losses() {
    pollster::block_on(async {
        let doc = demo::build(
            DemoKind::Optimization,
            &Device::flex(),
            TraceLimits::default(),
        )
        .await
        .unwrap();
        assert_eq!(doc.optimization.len(), 25);
        assert_eq!(doc.trace.frames.len(), 25);
        for (i, (p, frame)) in doc.optimization.iter().zip(&doc.trace.frames).enumerate() {
            near(p.loss, p.parameters[0].cos());
            near(p.loss, frame.probabilities[0] - frame.probabilities[1]);
            assert_eq!(p.loss_evaluations, 1 + 3 * i);
            assert_eq!(p.fidelity_evaluations, 4 * i);
            assert_eq!(p.probabilities, frame.probabilities);
        }
        assert!(doc.optimization.last().unwrap().loss < -0.98);
        assert!(doc.optimization.last().unwrap().loss < doc.optimization[0].loss);
        doc.validate().unwrap();
    });
}

#[test]
fn actual_photonic_schedule_and_untrusted_document_checks_are_bounded() {
    pollster::block_on(async {
        let device = Device::flex();
        let limits = TraceLimits::default();
        let doc = demo::build(DemoKind::PhotonicRouting, &device, limits)
            .await
            .unwrap();
        let fabric = doc.fabric.as_ref().unwrap();
        assert_eq!(
            (
                fabric.resource_count,
                fabric.photon_count,
                fabric.routes.len(),
                fabric.fusions.len()
            ),
            (2, 4, 2, 1)
        );
        assert_eq!(fabric.outputs, vec![0, 3]);
        assert!(fabric.routes.iter().all(|r| r.transmission[0] < 1.));
        let mut bad = doc.clone();
        bad.fabric.as_mut().unwrap().routes[0].hops[0].arrival += 1;
        assert!(bad.validate().is_err());
        let mut bad = doc.clone();
        bad.fabric.as_mut().unwrap().routes[0].transmission = [1.0; 2];
        assert!(bad.validate().is_err());
        let mut bad = doc.clone();
        bad.fabric.as_mut().unwrap().nodes[0].position[0] = f32::MAX;
        assert!(bad.validate().is_err());
        let mut bad = doc.clone();
        bad.trace.frames[0].probabilities[0] = f64::NAN;
        assert!(bad.validate().is_err());
        let mut bad = doc.clone();
        bad.trace.frames[0].subsystems[0].density[0].re = 0.;
        assert!(bad.validate().is_err());
        let mut bad = doc.clone();
        bad.trace.frames[0].amplitudes.as_mut().unwrap()[0].im = 0.1;
        assert!(bad.validate().is_err());
        let text = serde_json::to_string(&doc).unwrap();
        let restored: burn_quantum_visualization::model::VisualizationDocument =
            serde_json::from_str(&text).unwrap();
        restored.validate().unwrap();
        for limits in [
            TraceLimits {
                max_frames: 1,
                ..limits
            },
            TraceLimits {
                max_dimension: 2,
                ..limits
            },
            TraceLimits {
                max_trace_elements: 1,
                ..limits
            },
            TraceLimits {
                max_matrix_elements: 1,
                ..limits
            },
        ] {
            assert!(demo::build(DemoKind::Bell, &device, limits).await.is_err());
        }
    });
}

#[test]
fn twelve_qubit_trace_and_bounded_plots_preserve_scientific_mass() {
    pollster::block_on(async {
        let device = Device::flex();
        let mut circuit = Circuit::new(Register::qubits(12).unwrap(), 0, 0);
        let mut labels = vec![];
        for wire in 0..12 {
            circuit.unitary(vec![wire], Unitary::h()).unwrap();
            labels.push(format!("H q{wire}"));
        }
        let trace =
            simulation::simulate(&circuit, &[], &[], &labels, &device, TraceLimits::default())
                .await
                .unwrap();
        assert_eq!(trace.frames.len(), 13);
        let output = trace.frames.last().unwrap();
        assert_eq!(output.probabilities.len(), 4096);
        for &probability in &output.probabilities {
            near(probability, 1. / 4096.);
        }
        let bins = aggregate_probabilities(&output.probabilities, 64).unwrap();
        assert_eq!(bins.len(), 64);
        near(bins.iter().map(|b| b.probability).sum(), 1.);
        for state in &output.subsystems {
            near(state.bloch.unwrap()[0], 1.);
            near(state.purity, 1.);
        }
    });
}

#[test]
fn heterogeneous_endianness_input_bindings_and_nonpositive_imports_are_checked() {
    pollster::block_on(async {
        use burn_quantum::{Angle, Rotation, gate::Axis};
        let device = Device::flex();
        let limits = TraceLimits::default();
        let mut circuit = Circuit::new(Register::new(vec![2, 3]).unwrap(), 0, 0);
        circuit.unitary(vec![0], Unitary::x()).unwrap();
        circuit
            .unitary(vec![1], Unitary::shift(3, 2).unwrap())
            .unwrap();
        let trace = simulation::simulate(
            &circuit,
            &[],
            &[],
            &["X".into(), "Shift2".into()],
            &device,
            limits,
        )
        .await
        .unwrap();
        let frame = trace.frames.last().unwrap();
        near(frame.probabilities[5], 1.);
        near(frame.subsystems[0].bloch.unwrap()[2], -1.);
        near(frame.subsystems[1].probabilities[2], 1.);
        let mut circuit = Circuit::new(Register::qubits(1).unwrap(), 0, 1);
        circuit
            .rotate(0, Rotation::qubit(Axis::Y, Angle::input(0)))
            .unwrap();
        let trace = simulation::simulate(
            &circuit,
            &[],
            &[std::f64::consts::FRAC_PI_2],
            &["Ry(input)".into()],
            &device,
            limits,
        )
        .await
        .unwrap();
        near(trace.frames[1].subsystems[0].bloch.unwrap()[0], 1.);
        assert!(
            simulation::simulate(&circuit, &[], &[], &["Ry(input)".into()], &device, limits)
                .await
                .is_err()
        );
        let mut doc = demo::build(DemoKind::Qutrit, &device, limits)
            .await
            .unwrap();
        let frame = doc.trace.frames.last_mut().unwrap();
        frame.amplitudes = None;
        frame.probabilities.fill(1. / 3.);
        frame.norm = 1.;
        let sub = &mut frame.subsystems[0];
        sub.probabilities.fill(1. / 3.);
        sub.density.fill(Default::default());
        for i in 0..3 {
            sub.density[3 * i + i].re = 1. / 3.;
        }
        sub.density[1].re = 0.4;
        sub.density[3].re = 0.4;
        sub.purity = 1. / 3. + 0.32;
        // Trace one and Hermitian with valid marginals, but a negative eigenvalue.
        assert!(doc.validate().is_err());
        assert!(aggregate_probabilities(&[f64::MAX, f64::MAX], 1).is_err());
    });
}

#[test]
fn fabric_locations_include_waiting_destructive_consumption_and_held_outputs() {
    use burn_quantum_visualization::model::PhotonLocation;
    pollster::block_on(async {
        let doc = demo::build(
            DemoKind::PhotonicRouting,
            &Device::flex(),
            TraceLimits::default(),
        )
        .await
        .unwrap();
        let f = doc.fabric.as_ref().unwrap();
        assert_eq!(f.resources.len(), 2);
        for r in &f.routes {
            for h in &r.hops {
                assert_eq!(
                    f.photon_location(r.photon, h.departure as f64 + 0.5),
                    Some(PhotonLocation::Link {
                        link: h.link,
                        progress: 0.5 / (h.arrival - h.departure) as f32
                    })
                );
            }
            let event = f
                .fusions
                .iter()
                .find(|e| e.photons.contains(&r.photon))
                .unwrap();
            assert_eq!(
                f.photon_location(r.photon, event.tick as f64),
                Some(PhotonLocation::Node(event.node))
            );
            assert_eq!(
                f.photon_location(r.photon, (event.tick + 1) as f64),
                Some(PhotonLocation::Consumed)
            );
        }
        assert_eq!(
            f.photon_location(0, f.finish_tick as f64),
            Some(PhotonLocation::Node(0))
        );
        assert_eq!(
            f.photon_location(3, f.finish_tick as f64),
            Some(PhotonLocation::Node(1))
        );
        let mut bad = doc.clone();
        bad.fabric.as_mut().unwrap().resources[0].photons[0] = 3;
        assert!(bad.validate().is_err());
    });
}