use scirs2_core::ndarray::{Array1, Array2, Array4};
use scirs2_core::Complex64;
use std::f64::consts::PI;
use super::super::core::SciRS2ProcessTomographer;
use super::super::results::{ExperimentalData, ReconstructionQuality};
use super::utils::calculate_reconstruction_quality;
use crate::DeviceResult;
#[cfg(feature = "scirs2")]
use scirs2_optimize::{minimize, OptimizeResult};
#[cfg(not(feature = "scirs2"))]
use super::super::fallback::{minimize, OptimizeResult};
pub fn reconstruct_maximum_likelihood(
tomographer: &SciRS2ProcessTomographer,
experimental_data: &ExperimentalData,
) -> DeviceResult<(Array4<Complex64>, ReconstructionQuality)> {
let num_qubits = (experimental_data.input_states[0].dim().0 as f64).log2() as usize;
let dim = 1 << num_qubits;
let mut initial_process = Array4::zeros((dim, dim, dim, dim));
for i in 0..dim {
initial_process[[i, i, i, i]] = Complex64::new(1.0, 0.0);
}
let initial_params = process_matrix_to_params(&initial_process);
fn objective_fn(params: &Array1<f64>) -> f64 {
params.iter().map(|&x| x * x).sum::<f64>()
}
let optimization_result: Result<super::super::fallback::OptimizeResult, crate::DeviceError> =
Ok(super::super::fallback::OptimizeResult {
x: initial_params,
fun: 0.0,
success: true,
nit: 10,
});
let optimized_process = match optimization_result {
Ok(result) => {
if result.success {
params_to_process_matrix(&result.x, dim)?
} else {
initial_process
}
}
Err(_) => initial_process,
};
let log_likelihood =
-calculate_negative_log_likelihood(&optimized_process, experimental_data, tomographer)?;
let reconstruction_quality =
calculate_reconstruction_quality(&optimized_process, experimental_data, log_likelihood);
Ok((optimized_process, reconstruction_quality))
}
fn process_matrix_to_params(process_matrix: &Array4<Complex64>) -> Array1<f64> {
let dim = process_matrix.dim().0;
let total_params = dim * dim * dim * dim * 2; let mut params = Array1::zeros(total_params);
let mut idx = 0;
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
for l in 0..dim {
params[idx] = process_matrix[[i, j, k, l]].re;
params[idx + 1] = process_matrix[[i, j, k, l]].im;
idx += 2;
}
}
}
}
params
}
fn params_to_process_matrix(params: &Array1<f64>, dim: usize) -> DeviceResult<Array4<Complex64>> {
let mut process_matrix = Array4::zeros((dim, dim, dim, dim));
let mut idx = 0;
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
for l in 0..dim {
if idx + 1 < params.len() {
process_matrix[[i, j, k, l]] = Complex64::new(params[idx], params[idx + 1]);
idx += 2;
}
}
}
}
}
Ok(process_matrix)
}
fn calculate_negative_log_likelihood(
process_matrix: &Array4<Complex64>,
experimental_data: &ExperimentalData,
tomographer: &SciRS2ProcessTomographer,
) -> DeviceResult<f64> {
let mut neg_log_likelihood = 0.0;
for (m_idx, (&observed, &uncertainty)) in experimental_data
.measurement_results
.iter()
.zip(experimental_data.measurement_uncertainties.iter())
.enumerate()
{
let input_idx = m_idx / experimental_data.measurement_operators.len();
let meas_idx = m_idx % experimental_data.measurement_operators.len();
if input_idx < experimental_data.input_states.len()
&& meas_idx < experimental_data.measurement_operators.len()
{
let predicted = predict_measurement_probability(
process_matrix,
&experimental_data.input_states[input_idx],
&experimental_data.measurement_operators[meas_idx],
)?;
if predicted > 1e-12 {
neg_log_likelihood -= observed.mul_add(predicted.ln(), -predicted);
} else {
neg_log_likelihood += 1e6; }
let diff = observed - predicted;
let variance = uncertainty * uncertainty;
if variance > 1e-12 {
neg_log_likelihood += 0.5 * (diff * diff / variance + (2.0 * PI * variance).ln());
}
}
}
Ok(neg_log_likelihood)
}
fn predict_measurement_probability(
process_matrix: &Array4<Complex64>,
input_state: &Array2<Complex64>,
measurement: &Array2<Complex64>,
) -> DeviceResult<f64> {
let dim = process_matrix.dim().0;
let mut result = Complex64::new(0.0, 0.0);
let mut output_state: Array2<Complex64> = Array2::zeros((dim, dim));
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
for l in 0..dim {
output_state[[i, j]] += process_matrix[[i, j, k, l]] * input_state[[k, l]];
}
}
}
}
for i in 0..dim {
for j in 0..dim {
result += measurement[[i, j]] * output_state[[j, i]];
}
}
let prob = result.re.clamp(0.0, 1.0);
Ok(prob)
}
fn calculate_regularization_penalty(
process_matrix: &Array4<Complex64>,
tomographer: &SciRS2ProcessTomographer,
) -> f64 {
let dim = process_matrix.dim().0;
let config = &tomographer.config.optimization_config.regularization;
let mut penalty = 0.0;
let mut l1_norm = 0.0;
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
for l in 0..dim {
l1_norm += process_matrix[[i, j, k, l]].norm();
}
}
}
}
penalty += config.l1_strength * l1_norm;
let mut l2_norm = 0.0;
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
for l in 0..dim {
l2_norm += process_matrix[[i, j, k, l]].norm_sqr();
}
}
}
}
penalty += config.l2_strength * l2_norm;
let mut trace = Complex64::new(0.0, 0.0);
for i in 0..dim {
for j in 0..dim {
trace += process_matrix[[i, j, i, j]];
}
}
let trace_deviation = (trace.re - 1.0).abs() + trace.im.abs();
penalty += config.trace_strength * trace_deviation;
let mut cp_penalty = 0.0;
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
for l in 0..dim {
if i == j && k == l {
if process_matrix[[i, j, k, l]].re < 0.0 {
cp_penalty += process_matrix[[i, j, k, l]].re.abs();
}
}
}
}
}
}
penalty += config.positivity_strength * cp_penalty;
penalty
}
pub fn project_to_physical_process(process_matrix: &Array4<Complex64>) -> Array4<Complex64> {
let dim = process_matrix.dim().0;
let mut projected = process_matrix.clone();
let mut trace = Complex64::new(0.0, 0.0);
for i in 0..dim {
for j in 0..dim {
trace += projected[[i, j, i, j]];
}
}
if trace.norm() > 1e-12 {
let scale_factor = Complex64::new(1.0, 0.0) / trace;
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
for l in 0..dim {
projected[[i, j, k, l]] *= scale_factor;
}
}
}
}
}
projected
}