Skip to main content

oxicuda_quantum/kernel/
quantum_kernel.rs

1use crate::embedding::zz_feature::zz_feature_map;
2use crate::error::{QuantumError, QuantumResult};
3
4/// Overlap kernel k(x, y) = |⟨ψ(x)|ψ(y)⟩|².
5///
6/// Both x and y are encoded via the ZZ feature map with 1 repetition.
7pub fn overlap_kernel(x: &[f32], y: &[f32]) -> QuantumResult<f32> {
8    if x.len() != y.len() {
9        return Err(QuantumError::DimensionMismatch {
10            expected: x.len(),
11            got: y.len(),
12        });
13    }
14    if x.is_empty() {
15        return Err(QuantumError::EmptyInput);
16    }
17
18    let psi_x = zz_feature_map(x, 1)?;
19    let psi_y = zz_feature_map(y, 1)?;
20
21    let ip = psi_x.inner_product(&psi_y)?;
22    Ok(ip.norm_sqr())
23}
24
25/// Compute the full kernel matrix K\[i,j\] = k(xs\[i\], xs\[j\]).
26pub fn kernel_matrix(xs: &[Vec<f32>]) -> QuantumResult<Vec<Vec<f32>>> {
27    if xs.is_empty() {
28        return Err(QuantumError::EmptyInput);
29    }
30    let n = xs.len();
31    let mut mat = vec![vec![0.0_f32; n]; n];
32
33    for i in 0..n {
34        for j in i..n {
35            let k = overlap_kernel(&xs[i], &xs[j])?;
36            mat[i][j] = k;
37            mat[j][i] = k;
38        }
39    }
40
41    Ok(mat)
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn self_kernel_is_one() {
50        let x = vec![0.5_f32, 1.0];
51        let k = overlap_kernel(&x, &x)
52            .expect("self-overlap of a non-empty, same-length vector pair cannot fail");
53        assert!((k - 1.0).abs() < 1e-4, "k={k}");
54    }
55
56    #[test]
57    fn kernel_matrix_diagonal_is_one() {
58        let xs = vec![vec![0.3_f32, 0.7], vec![1.0_f32, -0.5]];
59        let mat = kernel_matrix(&xs)
60            .expect("kernel matrix of non-empty, equal-length vectors cannot fail");
61        assert!((mat[0][0] - 1.0).abs() < 1e-4);
62        assert!((mat[1][1] - 1.0).abs() < 1e-4);
63    }
64}