use super::EncodingStrategy;
use crate::{
error::{QuantRS2Error, QuantRS2Result},
gate::{single::Hadamard, GateOp},
parametric::{ParametricRotationY, ParametricRotationZ},
qubit::QubitId,
};
use scirs2_core::Complex64;
use std::f64::consts::PI;
#[allow(dead_code)]
type RYGate = ParametricRotationY;
#[allow(dead_code)]
type RZGate = ParametricRotationZ;
#[derive(Debug, Clone, Copy)]
struct CNOT {
control: QubitId,
target: QubitId,
}
impl GateOp for CNOT {
fn name(&self) -> &'static str {
"CNOT"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.control, self.target]
}
fn matrix(&self) -> crate::error::QuantRS2Result<Vec<Complex64>> {
Ok(vec![
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
])
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(*self)
}
}
pub struct DataEncoder {
strategy: EncodingStrategy,
num_qubits: usize,
num_features: usize,
}
impl DataEncoder {
pub const fn new(strategy: EncodingStrategy, num_qubits: usize) -> Self {
let num_features = match strategy {
EncodingStrategy::Amplitude => 1 << num_qubits, EncodingStrategy::Angle | EncodingStrategy::Basis => num_qubits, EncodingStrategy::IQP => num_qubits * (num_qubits + 1) / 2, };
Self {
strategy,
num_qubits,
num_features,
}
}
pub const fn num_features(&self) -> usize {
self.num_features
}
pub fn encode(&self, data: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
if data.len() != self.num_features {
return Err(QuantRS2Error::InvalidInput(format!(
"Expected {} features, got {}",
self.num_features,
data.len()
)));
}
match self.strategy {
EncodingStrategy::Amplitude => self.amplitude_encoding(data),
EncodingStrategy::Angle => self.angle_encoding(data),
EncodingStrategy::IQP => self.iqp_encoding(data),
EncodingStrategy::Basis => self.basis_encoding(data),
}
}
fn amplitude_encoding(&self, data: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
let norm: f64 = data.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm < 1e-10 {
return Err(QuantRS2Error::InvalidInput(
"Cannot encode zero vector".to_string(),
));
}
let normalized: Vec<f64> = data.iter().map(|x| x / norm).collect();
let mut gates: Vec<Box<dyn GateOp>> = vec![];
let angles = compute_amplitude_angles(&normalized);
if let Some(&angle) = angles.first() {
gates.push(Box::new(ParametricRotationY::new(QubitId(0), angle)));
}
for level in 1..self.num_qubits {
let num_angles_at_level = 1usize << level;
let level_start = num_angles_at_level - 1; let level_end = level_start + num_angles_at_level;
let level_angles = if level_end <= angles.len() {
&angles[level_start..level_end]
} else {
continue;
};
let ucry_angles = ucry_decompose(level_angles);
let target = QubitId(level as u32);
for (k, &theta) in ucry_angles.iter().enumerate() {
gates.push(Box::new(ParametricRotationY::new(target, theta)));
if k < ucry_angles.len().saturating_sub(1) {
let gray_k = k ^ (k >> 1);
let gray_k1 = (k + 1) ^ ((k + 1) >> 1);
let diff_bit = gray_k ^ gray_k1;
let ctrl_qubit = diff_bit.trailing_zeros();
if (ctrl_qubit as usize) < level {
gates.push(Box::new(CNOT {
control: QubitId(ctrl_qubit),
target,
}));
}
}
}
}
Ok(gates)
}
fn angle_encoding(&self, data: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
let mut gates: Vec<Box<dyn GateOp>> = vec![];
for i in 0..self.num_qubits {
gates.push(Box::new(Hadamard {
target: QubitId(i as u32),
}));
}
for (i, &value) in data.iter().enumerate() {
let qubit = QubitId(i as u32);
let angle = value * PI;
gates.push(Box::new(ParametricRotationY::new(qubit, angle)));
}
Ok(gates)
}
fn iqp_encoding(&self, data: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
let mut gates: Vec<Box<dyn GateOp>> = vec![];
for i in 0..self.num_qubits {
gates.push(Box::new(Hadamard {
target: QubitId(i as u32),
}));
}
for i in 0..self.num_qubits {
let angle = data[i] * PI;
gates.push(Box::new(ParametricRotationZ::new(QubitId(i as u32), angle)));
}
let mut idx = self.num_qubits;
for i in 0..self.num_qubits {
for j in i + 1..self.num_qubits {
if idx < data.len() {
let angle = data[idx] * PI;
gates.push(Box::new(CNOT {
control: QubitId(i as u32),
target: QubitId(j as u32),
}));
gates.push(Box::new(ParametricRotationZ::new(QubitId(j as u32), angle)));
gates.push(Box::new(CNOT {
control: QubitId(i as u32),
target: QubitId(j as u32),
}));
idx += 1;
}
}
}
Ok(gates)
}
fn basis_encoding(&self, data: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
use crate::gate::single::PauliX;
let mut gates: Vec<Box<dyn GateOp>> = vec![];
for (i, &value) in data.iter().enumerate() {
if value.abs() > 0.5 {
gates.push(Box::new(PauliX {
target: QubitId(i as u32),
}));
}
}
Ok(gates)
}
}
pub struct FeatureMap {
num_qubits: usize,
num_features: usize,
map_type: FeatureMapType,
reps: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum FeatureMapType {
Pauli,
ZFeature,
ZZFeature,
Custom,
}
impl FeatureMap {
pub const fn new(num_qubits: usize, map_type: FeatureMapType, reps: usize) -> Self {
let num_features = num_qubits;
Self {
num_qubits,
num_features,
map_type,
reps,
}
}
pub fn create_gates(&self, features: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
if features.len() != self.num_features {
return Err(QuantRS2Error::InvalidInput(format!(
"Expected {} features, got {}",
self.num_features,
features.len()
)));
}
let mut gates = vec![];
for _ in 0..self.reps {
match self.map_type {
FeatureMapType::Pauli => {
gates.extend(self.pauli_feature_map(features)?);
}
FeatureMapType::ZFeature => {
gates.extend(self.z_feature_map(features)?);
}
FeatureMapType::ZZFeature => {
gates.extend(self.zz_feature_map(features)?);
}
FeatureMapType::Custom => {
}
}
}
Ok(gates)
}
fn pauli_feature_map(&self, features: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
let mut gates: Vec<Box<dyn GateOp>> = vec![];
for i in 0..self.num_qubits {
gates.push(Box::new(Hadamard {
target: QubitId(i as u32),
}));
}
for (i, &feature) in features.iter().enumerate() {
gates.push(Box::new(ParametricRotationZ::new(
QubitId(i as u32),
2.0 * feature,
)));
}
Ok(gates)
}
fn z_feature_map(&self, features: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
let mut gates: Vec<Box<dyn GateOp>> = vec![];
for i in 0..self.num_qubits {
gates.push(Box::new(Hadamard {
target: QubitId(i as u32),
}));
}
for (i, &feature) in features.iter().enumerate() {
gates.push(Box::new(ParametricRotationZ::new(
QubitId(i as u32),
2.0 * feature,
)));
}
Ok(gates)
}
fn zz_feature_map(&self, features: &[f64]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
let mut gates = self.z_feature_map(features)?;
for i in 0..self.num_qubits - 1 {
gates.push(Box::new(CNOT {
control: QubitId(i as u32),
target: QubitId((i + 1) as u32),
}));
let angle = (PI - features[i]) * (PI - features[i + 1]);
gates.push(Box::new(ParametricRotationZ::new(
QubitId((i + 1) as u32),
angle,
)));
gates.push(Box::new(CNOT {
control: QubitId(i as u32),
target: QubitId((i + 1) as u32),
}));
}
Ok(gates)
}
}
pub struct DataReuploader {
encoder: DataEncoder,
num_layers: usize,
trainable_scaling: bool,
}
impl DataReuploader {
pub const fn new(encoder: DataEncoder, num_layers: usize, trainable_scaling: bool) -> Self {
Self {
encoder,
num_layers,
trainable_scaling,
}
}
pub fn create_gates(
&self,
data: &[f64],
scaling_params: Option<&[f64]>,
) -> QuantRS2Result<Vec<Vec<Box<dyn GateOp>>>> {
let mut layers = vec![];
for layer in 0..self.num_layers {
let scaled_data = if self.trainable_scaling {
if let Some(params) = scaling_params {
let offset = layer * data.len();
if offset + data.len() > params.len() {
return Err(QuantRS2Error::InvalidInput(
"Not enough scaling parameters".to_string(),
));
}
data.iter()
.zip(¶ms[offset..offset + data.len()])
.map(|(d, p)| d * p)
.collect()
} else {
data.to_vec()
}
} else {
data.to_vec()
};
layers.push(self.encoder.encode(&scaled_data)?);
}
Ok(layers)
}
}
fn compute_amplitude_angles(amplitudes: &[f64]) -> Vec<f64> {
let n = amplitudes.len();
if n == 0 {
return vec![];
}
let mut angles: Vec<f64> = Vec::new();
let mut current_level: Vec<f64> = amplitudes.to_vec();
while current_level.len() > 1 {
let mut next_level: Vec<f64> = Vec::with_capacity(current_level.len() / 2);
let mut level_angles: Vec<f64> = Vec::with_capacity(current_level.len() / 2);
let chunks = current_level.chunks(2);
for chunk in chunks {
let left = chunk[0];
let right = if chunk.len() > 1 { chunk[1] } else { 0.0 };
let norm = (left * left + right * right).sqrt();
next_level.push(norm);
let angle = 2.0 * right.atan2(left);
level_angles.push(angle);
}
angles.extend(level_angles);
current_level = next_level;
}
angles.reverse();
angles
}
fn ucry_decompose(angles: &[f64]) -> Vec<f64> {
let n = angles.len();
if n == 0 {
return vec![];
}
let mut result = angles.to_vec();
let mut step = 1usize;
while step < n {
let mut k = 0usize;
while k < n {
for j in 0..step {
let a = result[k + j];
let b = result[k + j + step];
result[k + j] = (a + b) / 2.0;
result[k + j + step] = (a - b) / 2.0;
}
k += 2 * step;
}
step *= 2;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_angle_encoding() {
let encoder = DataEncoder::new(EncodingStrategy::Angle, 3);
assert_eq!(encoder.num_features(), 3);
let data = vec![0.5, 1.0, 0.0];
let gates = encoder.encode(&data).expect("Failed to encode angle data");
assert_eq!(gates.len(), 6);
}
#[test]
fn test_basis_encoding() {
let encoder = DataEncoder::new(EncodingStrategy::Basis, 4);
assert_eq!(encoder.num_features(), 4);
let data = vec![1.0, 0.0, 1.0, 0.0];
let gates = encoder.encode(&data).expect("Failed to encode basis data");
assert_eq!(gates.len(), 2);
}
#[test]
fn test_feature_map() {
let feature_map = FeatureMap::new(2, FeatureMapType::ZFeature, 1);
let features = vec![0.5, 0.7];
let gates = feature_map
.create_gates(&features)
.expect("Failed to create feature map gates");
assert_eq!(gates.len(), 4);
}
#[test]
fn test_data_reuploader() {
let encoder = DataEncoder::new(EncodingStrategy::Angle, 2);
let reuploader = DataReuploader::new(encoder, 3, false);
let data = vec![0.5, 0.5];
let layers = reuploader
.create_gates(&data, None)
.expect("Failed to create reuploader gates");
assert_eq!(layers.len(), 3); assert_eq!(layers[0].len(), 4); }
}