use crate::error::{MLError, Result};
use crate::simulator_backends::{DynamicCircuit, Observable, SimulationResult, SimulatorBackend};
use quantrs2_circuit::prelude::*;
use quantrs2_core::prelude::*;
use scirs2_core::ndarray::{s, Array1, Array2, Array3, Array4, ArrayD, Axis};
use std::collections::HashMap;
use super::types::{DataEncodingType, TFQCircuitFormat, TFQGate};
pub trait TFQLayer: Send + Sync {
fn forward(&self, inputs: &ArrayD<f64>) -> Result<ArrayD<f64>>;
fn backward(&self, upstream_gradients: &ArrayD<f64>) -> Result<ArrayD<f64>>;
fn get_parameters(&self) -> Vec<Array1<f64>>;
fn set_parameters(&mut self, params: Vec<Array1<f64>>) -> Result<()>;
fn name(&self) -> &str;
}
pub mod tfq_utils {
use super::*;
pub fn circuit_to_tfq_format(circuit: &DynamicCircuit) -> Result<TFQCircuitFormat> {
let tfq_gates: Vec<TFQGate> = Vec::new();
Ok(TFQCircuitFormat {
gates: tfq_gates,
num_qubits: circuit.num_qubits(),
})
}
pub fn create_data_encoding_circuit(
num_qubits: usize,
encoding_type: DataEncodingType,
) -> Result<DynamicCircuit> {
let mut builder: Circuit<8> = CircuitBuilder::new();
match encoding_type {
DataEncodingType::Amplitude => {
for qubit in 0..num_qubits {
builder.ry(qubit, 0.0)?;
}
}
DataEncodingType::Angle => {
for qubit in 0..num_qubits {
builder.rz(qubit, 0.0)?;
}
}
DataEncodingType::Basis => {
for qubit in 0..num_qubits {
builder.x(qubit)?;
}
}
}
let circuit = builder.build();
DynamicCircuit::from_circuit(circuit)
}
pub fn create_hardware_efficient_ansatz(
num_qubits: usize,
layers: usize,
) -> Result<DynamicCircuit> {
let mut builder: Circuit<8> = CircuitBuilder::new();
for layer in 0..layers {
for qubit in 0..num_qubits {
builder.ry(qubit, 0.0)?;
builder.rz(qubit, 0.0)?;
}
for qubit in 0..num_qubits - 1 {
builder.cnot(qubit, qubit + 1)?;
}
if layer < layers - 1 && num_qubits > 2 {
builder.cnot(num_qubits - 1, 0)?;
}
}
let circuit = builder.build();
DynamicCircuit::from_circuit(circuit)
}
pub fn batch_execute_circuits(
circuits: &[DynamicCircuit],
parameters: &Array2<f64>,
observables: &[Observable],
backend: &dyn SimulatorBackend,
) -> Result<Array2<f64>> {
let batch_size = circuits.len();
let num_observables = observables.len();
let mut results = Array2::zeros((batch_size, num_observables));
for (circuit_idx, circuit) in circuits.iter().enumerate() {
let params = parameters.row(circuit_idx % parameters.nrows());
let params_slice = params.as_slice().ok_or_else(|| {
MLError::InvalidConfiguration("Parameters must be contiguous in memory".to_string())
})?;
for (obs_idx, observable) in observables.iter().enumerate() {
let expectation = backend.expectation_value(circuit, params_slice, observable)?;
results[[circuit_idx, obs_idx]] = expectation;
}
}
Ok(results)
}
}
pub trait Differentiator: Send + Sync {
fn differentiate(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
observable: &Observable,
backend: &dyn SimulatorBackend,
) -> Result<Vec<f64>>;
fn name(&self) -> &str;
}
pub fn resolve_symbols(
circuit: &DynamicCircuit,
symbols: &[String],
values: &[f64],
) -> Result<DynamicCircuit> {
if symbols.len() != values.len() {
return Err(MLError::InvalidConfiguration(
"Number of symbols must match number of values".to_string(),
));
}
let mut _symbol_map = HashMap::new();
for (sym, &val) in symbols.iter().zip(values.iter()) {
_symbol_map.insert(sym.clone(), val);
}
Ok(circuit.clone())
}
pub fn tensor_to_circuits(tensor: &Array1<String>) -> Result<Vec<DynamicCircuit>> {
tensor
.iter()
.map(|_| DynamicCircuit::from_circuit::<8>(Circuit::<8>::new()))
.collect()
}
pub fn circuits_to_tensor(circuits: &[DynamicCircuit]) -> Array1<String> {
Array1::from_vec(
circuits
.iter()
.map(|c| format!("circuit_{}_qubits", c.num_qubits()))
.collect(),
)
}
pub mod cirq_converter {
use super::*;
use quantrs2_circuit::prelude::*;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum CirqGate {
X { qubit: usize },
Y { qubit: usize },
Z { qubit: usize },
H { qubit: usize },
S { qubit: usize },
T { qubit: usize },
CNOT { control: usize, target: usize },
CZ { control: usize, target: usize },
SWAP { qubit1: usize, qubit2: usize },
Rx { qubit: usize, angle: f64 },
Ry { qubit: usize, angle: f64 },
Rz { qubit: usize, angle: f64 },
U3 {
qubit: usize,
theta: f64,
phi: f64,
lambda: f64,
},
XPowGate {
qubit: usize,
exponent: f64,
global_shift: f64,
},
YPowGate {
qubit: usize,
exponent: f64,
global_shift: f64,
},
ZPowGate {
qubit: usize,
exponent: f64,
global_shift: f64,
},
Measure { qubits: Vec<usize> },
}
#[derive(Debug, Clone)]
pub struct CirqCircuit {
pub num_qubits: usize,
pub gates: Vec<CirqGate>,
pub param_symbols: HashMap<String, usize>,
}
impl CirqCircuit {
pub fn new(num_qubits: usize) -> Self {
Self {
num_qubits,
gates: Vec::new(),
param_symbols: HashMap::new(),
}
}
pub fn add_gate(&mut self, gate: CirqGate) {
self.gates.push(gate);
}
pub fn add_param_symbol(&mut self, symbol: String, index: usize) {
self.param_symbols.insert(symbol, index);
}
pub fn to_quantrs2_circuit<const N: usize>(&self) -> Result<Circuit<N>> {
if self.num_qubits != N {
return Err(MLError::ValidationError(format!(
"Circuit has {} qubits but expected {}",
self.num_qubits, N
)));
}
let mut builder = CircuitBuilder::new();
for gate in &self.gates {
match gate {
CirqGate::X { qubit } => {
builder.x(*qubit)?;
}
CirqGate::Y { qubit } => {
builder.y(*qubit)?;
}
CirqGate::Z { qubit } => {
builder.z(*qubit)?;
}
CirqGate::H { qubit } => {
builder.h(*qubit)?;
}
CirqGate::S { qubit } => {
builder.s(*qubit)?;
}
CirqGate::T { qubit } => {
builder.t(*qubit)?;
}
CirqGate::CNOT { control, target } => {
builder.cnot(*control, *target)?;
}
CirqGate::CZ { control, target } => {
builder.cz(*control, *target)?;
}
CirqGate::SWAP { qubit1, qubit2 } => {
builder.swap(*qubit1, *qubit2)?;
}
CirqGate::Rx { qubit, angle } => {
builder.rx(*qubit, *angle)?;
}
CirqGate::Ry { qubit, angle } => {
builder.ry(*qubit, *angle)?;
}
CirqGate::Rz { qubit, angle } => {
builder.rz(*qubit, *angle)?;
}
CirqGate::U3 {
qubit,
theta,
phi,
lambda,
} => {
builder.u(*qubit, *theta, *phi, *lambda)?;
}
CirqGate::XPowGate {
qubit,
exponent,
global_shift,
} => {
let angle = std::f64::consts::PI * exponent;
builder.rx(*qubit, angle)?;
let _ = global_shift;
}
CirqGate::YPowGate {
qubit,
exponent,
global_shift,
} => {
let angle = std::f64::consts::PI * exponent;
builder.ry(*qubit, angle)?;
let _ = global_shift;
}
CirqGate::ZPowGate {
qubit,
exponent,
global_shift,
} => {
let angle = std::f64::consts::PI * exponent;
builder.rz(*qubit, angle)?;
let _ = global_shift;
}
CirqGate::Measure { qubits: _ } => {}
}
}
Ok(builder.build())
}
pub fn to_dynamic_circuit(&self) -> Result<DynamicCircuit> {
match self.num_qubits {
1 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<1>()?),
2 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<2>()?),
3 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<3>()?),
4 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<4>()?),
5 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<5>()?),
6 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<6>()?),
7 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<7>()?),
8 => DynamicCircuit::from_circuit(self.to_quantrs2_circuit::<8>()?),
n => Err(MLError::ValidationError(format!(
"Unsupported qubit count: {}. Supported: 1-8",
n
))),
}
}
}
pub fn create_bell_circuit() -> CirqCircuit {
let mut circuit = CirqCircuit::new(2);
circuit.add_gate(CirqGate::H { qubit: 0 });
circuit.add_gate(CirqGate::CNOT {
control: 0,
target: 1,
});
circuit
}
pub fn create_parametric_circuit(num_qubits: usize, depth: usize) -> CirqCircuit {
let mut circuit = CirqCircuit::new(num_qubits);
for layer in 0..depth {
for qubit in 0..num_qubits {
let symbol = format!("theta_{}_{}", layer, qubit);
circuit.add_param_symbol(symbol.clone(), layer * num_qubits + qubit);
circuit.add_gate(CirqGate::Ry { qubit, angle: 0.5 });
}
for qubit in 0..num_qubits - 1 {
circuit.add_gate(CirqGate::CNOT {
control: qubit,
target: qubit + 1,
});
}
}
circuit
}
pub fn pow_gate_to_angle(exponent: f64) -> f64 {
std::f64::consts::PI * exponent
}
}