1use crate::error::{QuantRS2Error, QuantRS2Result};
10use crate::qubit::QubitId;
11use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2};
12use scirs2_core::random::prelude::*;
13use scirs2_core::Complex64;
14use std::fmt::Debug;
15
16pub trait QuantumOperation: Debug + Send + Sync {
18 fn apply_to_state(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<OperationResult>;
20
21 fn apply_to_density_matrix(
23 &self,
24 rho: &ArrayView2<Complex64>,
25 ) -> QuantRS2Result<Array2<Complex64>>;
26
27 fn qubits(&self) -> Vec<QubitId>;
29
30 fn is_deterministic(&self) -> bool;
32}
33
34#[derive(Debug, Clone)]
36pub enum OperationResult {
37 Deterministic(Array1<Complex64>),
39 Probabilistic {
41 outcome: usize,
42 probability: f64,
43 state: Array1<Complex64>,
44 },
45 MultiOutcome(Vec<MeasurementOutcome>),
47}
48
49#[derive(Debug, Clone)]
51pub struct MeasurementOutcome {
52 pub outcome: usize,
54 pub probability: f64,
56 pub state: Array1<Complex64>,
58}
59
60#[derive(Debug, Clone)]
62pub struct ProjectiveMeasurement {
63 pub qubits: Vec<QubitId>,
65 pub outcome: Option<usize>,
67}
68
69impl ProjectiveMeasurement {
70 pub const fn new(qubits: Vec<QubitId>) -> Self {
72 Self {
73 qubits,
74 outcome: None,
75 }
76 }
77
78 pub const fn with_outcome(qubits: Vec<QubitId>, outcome: usize) -> Self {
80 Self {
81 qubits,
82 outcome: Some(outcome),
83 }
84 }
85
86 pub fn get_probabilities(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<Vec<f64>> {
88 let n_qubits = (state.len() as f64).log2() as usize;
89 let n_outcomes = 1 << self.qubits.len();
90 let mut probabilities = vec![0.0; n_outcomes];
91
92 for (idx, amp) in state.iter().enumerate() {
94 let outcome = self.extract_outcome_from_index(idx, n_qubits);
95 probabilities[outcome] += amp.norm_sqr();
96 }
97
98 Ok(probabilities)
99 }
100
101 fn extract_outcome_from_index(&self, index: usize, total_qubits: usize) -> usize {
103 let mut outcome = 0;
104 for (i, &qubit) in self.qubits.iter().enumerate() {
105 let bit = (index >> (total_qubits - 1 - qubit.0 as usize)) & 1;
106 outcome |= bit << (self.qubits.len() - 1 - i);
107 }
108 outcome
109 }
110
111 fn project_onto_outcome(
113 &self,
114 state: &ArrayView1<Complex64>,
115 outcome: usize,
116 ) -> QuantRS2Result<(f64, Array1<Complex64>)> {
117 let n_qubits = (state.len() as f64).log2() as usize;
118 let mut projected = Array1::zeros(state.len());
119 let mut norm_squared = 0.0;
120
121 for (idx, &) in state.iter().enumerate() {
122 if self.extract_outcome_from_index(idx, n_qubits) == outcome {
123 projected[idx] = amp;
124 norm_squared += amp.norm_sqr();
125 }
126 }
127
128 if norm_squared < 1e-10 {
129 return Err(QuantRS2Error::InvalidInput(
130 "Measurement outcome has zero probability".to_string(),
131 ));
132 }
133
134 let norm = norm_squared.sqrt();
136 projected = projected / norm;
137
138 Ok((norm_squared, projected))
139 }
140}
141
142impl QuantumOperation for ProjectiveMeasurement {
143 fn apply_to_state(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<OperationResult> {
144 if let Some(outcome) = self.outcome {
145 let (prob, new_state) = self.project_onto_outcome(state, outcome)?;
147 Ok(OperationResult::Probabilistic {
148 outcome,
149 probability: prob,
150 state: new_state,
151 })
152 } else {
153 let probabilities = self.get_probabilities(state)?;
155 let mut outcomes = Vec::new();
156
157 for (outcome, &prob) in probabilities.iter().enumerate() {
158 if prob > 1e-10 {
159 let (_, new_state) = self.project_onto_outcome(state, outcome)?;
160 outcomes.push(MeasurementOutcome {
161 outcome,
162 probability: prob,
163 state: new_state,
164 });
165 }
166 }
167
168 Ok(OperationResult::MultiOutcome(outcomes))
169 }
170 }
171
172 fn apply_to_density_matrix(
173 &self,
174 rho: &ArrayView2<Complex64>,
175 ) -> QuantRS2Result<Array2<Complex64>> {
176 let n_qubits = (rho.nrows() as f64).log2() as usize;
177 let mut result = Array2::zeros(rho.raw_dim());
178
179 for outcome in 0..(1 << self.qubits.len()) {
181 let projector = self.create_projector(outcome, n_qubits)?;
182 let proj_rho = projector.dot(rho).dot(&projector);
183 result = result + proj_rho;
184 }
185
186 Ok(result)
187 }
188
189 fn qubits(&self) -> Vec<QubitId> {
190 self.qubits.clone()
191 }
192
193 fn is_deterministic(&self) -> bool {
194 false
195 }
196}
197
198impl ProjectiveMeasurement {
199 fn create_projector(
201 &self,
202 outcome: usize,
203 total_qubits: usize,
204 ) -> QuantRS2Result<Array2<Complex64>> {
205 let dim = 1 << total_qubits;
206 let mut projector = Array2::zeros((dim, dim));
207
208 for idx in 0..dim {
209 if self.extract_outcome_from_index(idx, total_qubits) == outcome {
210 projector[[idx, idx]] = Complex64::new(1.0, 0.0);
211 }
212 }
213
214 Ok(projector)
215 }
216}
217
218#[derive(Debug, Clone)]
220pub struct POVMMeasurement {
221 pub elements: Vec<Array2<Complex64>>,
223 pub qubits: Vec<QubitId>,
225}
226
227impl POVMMeasurement {
228 pub fn new(elements: Vec<Array2<Complex64>>, qubits: Vec<QubitId>) -> QuantRS2Result<Self> {
230 let dim = elements[0].nrows();
232 let mut sum = Array2::<Complex64>::zeros((dim, dim));
233
234 for element in &elements {
235 if element.nrows() != dim || element.ncols() != dim {
236 return Err(QuantRS2Error::InvalidInput(
237 "All POVM elements must have the same dimension".to_string(),
238 ));
239 }
240 sum = sum + element;
241 }
242
243 for i in 0..dim {
245 for j in 0..dim {
246 let expected = if i == j {
247 Complex64::new(1.0, 0.0)
248 } else {
249 Complex64::new(0.0, 0.0)
250 };
251 let diff: Complex64 = sum[[i, j]] - expected;
252 if diff.norm_sqr().sqrt() > 1e-10 {
253 return Err(QuantRS2Error::InvalidInput(
254 "POVM elements do not sum to identity".to_string(),
255 ));
256 }
257 }
258 }
259
260 Ok(Self { elements, qubits })
261 }
262
263 pub fn get_probabilities(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<Vec<f64>> {
265 let mut probabilities = Vec::new();
266
267 for element in &self.elements {
268 let temp = element.dot(state);
270 let prob = state
271 .iter()
272 .zip(temp.iter())
273 .map(|(&psi, &m_psi)| psi.conj() * m_psi)
274 .sum::<Complex64>()
275 .re;
276 probabilities.push(prob);
277 }
278
279 Ok(probabilities)
280 }
281}
282
283impl QuantumOperation for POVMMeasurement {
284 fn apply_to_state(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<OperationResult> {
285 let probabilities = self.get_probabilities(state)?;
286 let mut outcomes = Vec::new();
287
288 for (i, (&prob, element)) in probabilities.iter().zip(&self.elements).enumerate() {
289 if prob > 1e-10 {
290 let new_state = element.dot(state) / prob.sqrt();
292 outcomes.push(MeasurementOutcome {
293 outcome: i,
294 probability: prob,
295 state: new_state,
296 });
297 }
298 }
299
300 Ok(OperationResult::MultiOutcome(outcomes))
301 }
302
303 fn apply_to_density_matrix(
304 &self,
305 rho: &ArrayView2<Complex64>,
306 ) -> QuantRS2Result<Array2<Complex64>> {
307 let mut result = Array2::zeros(rho.raw_dim());
308
309 for element in &self.elements {
311 let m_dag = element.t().mapv(|x| x.conj());
312 let term = element.dot(rho).dot(&m_dag);
313 result = result + term;
314 }
315
316 Ok(result)
317 }
318
319 fn qubits(&self) -> Vec<QubitId> {
320 self.qubits.clone()
321 }
322
323 fn is_deterministic(&self) -> bool {
324 false
325 }
326}
327
328#[derive(Debug, Clone)]
330pub struct Reset {
331 pub qubits: Vec<QubitId>,
333}
334
335impl Reset {
336 pub const fn new(qubits: Vec<QubitId>) -> Self {
338 Self { qubits }
339 }
340}
341
342impl QuantumOperation for Reset {
343 fn apply_to_state(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<OperationResult> {
344 let n_qubits = (state.len() as f64).log2() as usize;
345 let mut new_state = Array1::zeros(state.len());
346
347 let mut norm_squared = 0.0;
349 for (idx, &) in state.iter().enumerate() {
350 let mut should_keep = true;
351 for &qubit in &self.qubits {
352 let bit = (idx >> (n_qubits - 1 - qubit.0 as usize)) & 1;
353 if bit != 0 {
354 should_keep = false;
355 break;
356 }
357 }
358 if should_keep {
359 new_state[idx] = amp;
360 norm_squared += amp.norm_sqr();
361 }
362 }
363
364 if norm_squared < 1e-10 {
365 new_state[0] = Complex64::new(1.0, 0.0);
367 } else {
368 new_state = new_state / norm_squared.sqrt();
369 }
370
371 Ok(OperationResult::Deterministic(new_state))
372 }
373
374 fn apply_to_density_matrix(
375 &self,
376 rho: &ArrayView2<Complex64>,
377 ) -> QuantRS2Result<Array2<Complex64>> {
378 let n_qubits = (rho.nrows() as f64).log2() as usize;
381 let mut result = Array2::zeros(rho.raw_dim());
382
383 for i in 0..rho.nrows() {
385 for j in 0..rho.ncols() {
386 let mut should_keep = true;
387 for &qubit in &self.qubits {
388 let bit_i = (i >> (n_qubits - 1 - qubit.0 as usize)) & 1;
389 let bit_j = (j >> (n_qubits - 1 - qubit.0 as usize)) & 1;
390 if bit_i != 0 || bit_j != 0 {
391 should_keep = false;
392 break;
393 }
394 }
395 if should_keep {
396 result[[i, j]] = rho[[i, j]];
397 }
398 }
399 }
400
401 let trace = (0..result.nrows()).map(|i| result[[i, i]].re).sum::<f64>();
403 if trace > 1e-10 {
404 result = result / trace;
405 }
406
407 Ok(result)
408 }
409
410 fn qubits(&self) -> Vec<QubitId> {
411 self.qubits.clone()
412 }
413
414 fn is_deterministic(&self) -> bool {
415 true
416 }
417}
418
419pub fn sample_outcome(probabilities: &[f64]) -> QuantRS2Result<usize> {
421 let mut rng = thread_rng();
422 let r: f64 = rng.gen();
423
424 let mut cumsum = 0.0;
425 for (i, &prob) in probabilities.iter().enumerate() {
426 cumsum += prob;
427 if r < cumsum {
428 return Ok(i);
429 }
430 }
431
432 Err(QuantRS2Error::ComputationError(
434 "Probabilities do not sum to 1".to_string(),
435 ))
436}
437
438pub fn apply_and_sample<O: QuantumOperation>(
440 operation: &O,
441 state: &ArrayView1<Complex64>,
442) -> QuantRS2Result<(usize, Array1<Complex64>)> {
443 match operation.apply_to_state(state)? {
444 OperationResult::Deterministic(new_state) => Ok((0, new_state)),
445 OperationResult::Probabilistic { outcome, state, .. } => Ok((outcome, state)),
446 OperationResult::MultiOutcome(outcomes) => {
447 let probabilities: Vec<f64> = outcomes.iter().map(|o| o.probability).collect();
448 let sampled_idx = sample_outcome(&probabilities)?;
449 let outcome = &outcomes[sampled_idx];
450 Ok((outcome.outcome, outcome.state.clone()))
451 }
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458
459 #[test]
460 fn test_projective_measurement() {
461 let state = Array1::from_vec(vec![
463 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
464 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
465 ]);
466
467 let measurement = ProjectiveMeasurement::new(vec![QubitId(0)]);
468 let probs = measurement
469 .get_probabilities(&state.view())
470 .expect("Failed to compute measurement probabilities");
471
472 assert!((probs[0] - 0.5).abs() < 1e-10);
473 assert!((probs[1] - 0.5).abs() < 1e-10);
474 }
475
476 #[test]
477 fn test_reset_operation() {
478 let state = Array1::from_vec(vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)]);
480
481 let reset = Reset::new(vec![QubitId(0)]);
482 let result = reset
483 .apply_to_state(&state.view())
484 .expect("Failed to apply reset operation to state");
485
486 match result {
487 OperationResult::Deterministic(new_state) => {
488 assert!((new_state[0].norm() - 1.0).abs() < 1e-10);
489 assert!(new_state[1].norm() < 1e-10);
490 }
491 _ => panic!("Reset should be deterministic"),
492 }
493 }
494}