Skip to main content

amplitude_encoding/
amplitude_encoding.rs

1//! Amplitude Encoding — Classical Data into Quantum Amplitudes
2//!
3//! Amplitude encoding maps a classical vector x ∈ ℝ^d to a quantum state
4//! |ψ⟩ = Σ_i (x_i / ‖x‖) |i⟩
5//!
6//! This represents d-dimensional data in only ⌈log₂(d)⌉ qubits, achieving
7//! an exponential reduction in storage compared to classical representations.
8//!
9//! This example:
10//!   1. Encodes several classical vectors into quantum amplitudes
11//!   2. Verifies normalisation (‖|ψ⟩‖ = 1)
12//!   3. Computes quantum fidelity between similar/different vectors
13//!   4. Shows inner product calculation (quantum dot product)
14//!
15//! Run with:
16//!   cargo run --example amplitude_encoding -p quantrs2-ml --all-features
17
18use quantrs2_ml::error::Result;
19use quantrs2_ml::utils::encoding::amplitude_encode;
20use scirs2_core::ndarray::{array, Array1};
21use scirs2_core::Complex64;
22
23fn main() -> Result<()> {
24    println!("=== Amplitude Encoding: Classical Data → Quantum State ===\n");
25
26    // ---- Example 1: Simple 4-dimensional vector → 2 qubits ----
27    println!("--- Example 1: 4D vector (2 qubits) ---");
28    let v1 = array![1.0f64, 2.0, 3.0, 4.0];
29    let norm_v1: f64 = v1.iter().map(|x| x * x).sum::<f64>().sqrt();
30
31    let encoded1 = amplitude_encode(&v1)?;
32    let norm_enc1: f64 = encoded1.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
33
34    println!("Input vector : {:?}", v1.as_slice().expect("slice"));
35    println!("Input norm   : {norm_v1:.6}");
36    println!("Encoded state:");
37    for (i, &amp) in encoded1.iter().enumerate() {
38        println!(
39            "  |{i:02b}⟩ : amplitude = {:+.4}{:+.4}i  (prob = {:.6})",
40            amp.re,
41            amp.im,
42            amp.norm_sqr()
43        );
44    }
45    println!("Encoded norm : {norm_enc1:.10}  (should be 1.0)");
46
47    assert!(
48        (norm_enc1 - 1.0).abs() < 1e-10,
49        "Encoded state must be normalised, got norm={norm_enc1}"
50    );
51
52    // ---- Example 2: 8-dimensional image patch → 3 qubits ----
53    println!("\n--- Example 2: 8D image patch (3 qubits) ---");
54    // Simulating an 8-pixel grayscale image patch
55    let image_patch = array![0.1f64, 0.5, 0.9, 0.3, 0.7, 0.2, 0.8, 0.4];
56    let encoded2 = amplitude_encode(&image_patch)?;
57    let norm_enc2: f64 = encoded2.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
58
59    println!(
60        "Image patch (8 pixels): {:?}",
61        image_patch.as_slice().expect("slice")
62    );
63    println!("Quantum state norms²  :");
64    for (i, &amp) in encoded2.iter().enumerate() {
65        let bar = "#".repeat((amp.norm_sqr() * 20.0).round() as usize);
66        println!("  |{i:03b}⟩ : {:.4}  {bar}", amp.norm_sqr());
67    }
68    println!("Norm check           : {norm_enc2:.10}  (should be 1.0)");
69
70    assert!((norm_enc2 - 1.0).abs() < 1e-10, "Norm must be 1.0");
71
72    // ---- Example 3: Quantum inner product (dot product kernel) ----
73    // |⟨ψ_a|ψ_b⟩|² = (a·b)² / (‖a‖² ‖b‖²) — quantum kernel
74    println!("\n--- Example 3: Quantum Inner Product ---");
75
76    let a = array![1.0f64, 0.0, 0.0, 0.0]; // basis vector e1
77    let b = array![1.0f64, 0.0, 0.0, 0.0]; // identical → fidelity = 1
78    let c = array![0.0f64, 1.0, 0.0, 0.0]; // orthogonal → fidelity = 0
79    let d = array![1.0f64, 1.0, 0.0, 0.0]; // 45° angle → fidelity = 0.5
80
81    let enc_a = amplitude_encode(&a)?;
82    let enc_b = amplitude_encode(&b)?;
83    let enc_c = amplitude_encode(&c)?;
84    let enc_d = amplitude_encode(&d)?;
85
86    let fidelity_ab = quantum_fidelity(&enc_a, &enc_b);
87    let fidelity_ac = quantum_fidelity(&enc_a, &enc_c);
88    let fidelity_ad = quantum_fidelity(&enc_a, &enc_d);
89
90    println!("  ⟨e1|e1⟩²  = {fidelity_ab:.6}  (expected 1.0 — identical)");
91    println!("  ⟨e1|e2⟩²  = {fidelity_ac:.6}  (expected 0.0 — orthogonal)");
92    println!("  ⟨e1|d ⟩²  = {fidelity_ad:.6}  (expected 0.5 — 45°)");
93
94    assert!(
95        (fidelity_ab - 1.0).abs() < 1e-10,
96        "Identical vectors: fidelity=1"
97    );
98    assert!(fidelity_ac < 1e-10, "Orthogonal vectors: fidelity=0");
99    assert!((fidelity_ad - 0.5).abs() < 0.01, "45° angle: fidelity≈0.5");
100
101    // ---- Example 4: Zero vector error handling ----
102    println!("\n--- Example 4: Error handling (zero vector) ---");
103    let zero = array![0.0f64, 0.0, 0.0, 0.0];
104    match amplitude_encode(&zero) {
105        Err(e) => println!("  Zero vector rejected: {e}  ✓"),
106        Ok(_) => panic!("Should have rejected zero vector"),
107    }
108
109    // ---- Summary ----
110    println!("\n=== Summary ===");
111    println!("  d=4  → 2 qubits (log₂(4) = 2)");
112    println!("  d=8  → 3 qubits (log₂(8) = 3)");
113    println!("  d=2^n data encoded in n qubits — exponential compression");
114    println!("  Quantum inner product computable in O(1) measurement shots");
115
116    println!("\nAll checks passed — OK");
117
118    Ok(())
119}
120
121/// Compute quantum fidelity |⟨ψ_a|ψ_b⟩|² between two encoded states
122fn quantum_fidelity(psi_a: &Array1<Complex64>, psi_b: &Array1<Complex64>) -> f64 {
123    // ⟨ψ_a|ψ_b⟩ = Σ_i conj(ψ_a[i]) * ψ_b[i]
124    let inner: Complex64 = psi_a
125        .iter()
126        .zip(psi_b.iter())
127        .map(|(&a, &b)| a.conj() * b)
128        .sum();
129    inner.norm_sqr()
130}