quantrs2_ml/utils/
encoding.rs

1//! Quantum encoding schemes for feature mapping
2
3use crate::error::{MLError, Result};
4use scirs2_core::ndarray::{Array1, Array2};
5use scirs2_core::Complex64;
6
7use super::*;
8/// Amplitude encoding: encode classical data into quantum amplitudes
9pub fn amplitude_encode(data: &Array1<f64>) -> Result<Array1<Complex64>> {
10    let norm: f64 = data.iter().map(|x| x * x).sum::<f64>().sqrt();
11    if norm < 1e-10 {
12        return Err(MLError::ComputationError(
13            "Cannot amplitude encode zero vector".to_string(),
14        ));
15    }
16    let encoded = data.mapv(|x| Complex64::new(x / norm, 0.0));
17    Ok(encoded)
18}
19/// Angle encoding: encode data as rotation angles
20pub fn angle_encode(data: &Array1<f64>, scale: f64) -> Result<Array1<f64>> {
21    let pi = std::f64::consts::PI;
22    let encoded = data.mapv(|x| (x * scale).rem_euclid(2.0 * pi));
23    Ok(encoded)
24}
25/// Basis encoding: encode integer data as computational basis states
26/// Each integer is encoded as a binary string representing a basis state
27pub fn basis_encode(data: &Array1<usize>, num_qubits: usize) -> Result<Array2<u8>> {
28    let max_val = 1 << num_qubits;
29    for &val in data.iter() {
30        if val >= max_val {
31            return Err(MLError::InvalidInput(format!(
32                "Value {} exceeds maximum {} for {} qubits",
33                val,
34                max_val - 1,
35                num_qubits
36            )));
37        }
38    }
39    let mut encoded = Array2::zeros((data.len(), num_qubits));
40    for (i, &val) in data.iter().enumerate() {
41        for j in 0..num_qubits {
42            encoded[(i, num_qubits - 1 - j)] = ((val >> j) & 1) as u8;
43        }
44    }
45    Ok(encoded)
46}
47/// Product encoding: encode data using tensor product of single-qubit rotations
48/// Each feature is encoded using RY rotation on a separate qubit
49pub fn product_encode(data: &Array1<f64>) -> Result<Array1<f64>> {
50    let pi = std::f64::consts::PI;
51    let min_val = data.iter().cloned().fold(f64::INFINITY, f64::min);
52    let max_val = data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
53    let range = max_val - min_val;
54    if range < 1e-10 {
55        return Ok(Array1::from_elem(data.len(), pi / 2.0));
56    }
57    let encoded = data.mapv(|x| ((x - min_val) / range) * pi);
58    Ok(encoded)
59}
60/// Dense angle encoding: encode multiple features per qubit using multiple rotations
61/// Uses RY and RZ rotations to encode 2 features per qubit
62pub fn dense_angle_encode(data: &Array1<f64>) -> Result<Array1<f64>> {
63    let pi = std::f64::consts::PI;
64    let encoded = data.mapv(|x| {
65        let normalized = (x.atan() / (pi / 2.0) + 1.0) * pi;
66        normalized
67    });
68    Ok(encoded)
69}
70/// IQP (Instantaneous Quantum Polynomial) encoding
71/// Encodes data using diagonal unitary gates with polynomial feature map
72pub fn iqp_encode(data: &Array1<f64>, degree: usize) -> Result<Array1<f64>> {
73    let n = data.len();
74    let mut encoded = Vec::new();
75    for &x in data.iter() {
76        encoded.push(x);
77    }
78    if degree >= 2 {
79        for i in 0..n {
80            for j in i..n {
81                encoded.push(data[i] * data[j]);
82            }
83        }
84    }
85    if degree >= 3 {
86        for i in 0..n {
87            for j in i..n {
88                for k in j..n {
89                    encoded.push(data[i] * data[j] * data[k]);
90                }
91            }
92        }
93    }
94    Ok(Array1::from_vec(encoded))
95}
96/// Pauli feature map encoding
97/// Encodes data using Pauli rotation gates with entanglement
98pub fn pauli_feature_map_encode(data: &Array1<f64>, reps: usize) -> Result<Array1<f64>> {
99    let pi = std::f64::consts::PI;
100    let n = data.len();
101    let mut encoded = Vec::new();
102    for _ in 0..reps {
103        for &x in data.iter() {
104            encoded.push(2.0 * x);
105        }
106        for i in 0..n {
107            for j in (i + 1)..n {
108                let angle = (pi - data[i]) * (pi - data[j]);
109                encoded.push(2.0 * angle);
110            }
111        }
112    }
113    Ok(Array1::from_vec(encoded))
114}