1use crate::error::{QuantRS2Error, QuantRS2Result};
10use crate::qubit::QubitId;
11use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2};
12use scirs2_core::Complex64;
13use scirs2_core::random::prelude::*;
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 fn new(qubits: Vec<QubitId>) -> Self {
72 Self {
73 qubits,
74 outcome: None,
75 }
76 }
77
78 pub 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 match self.outcome {
145 Some(outcome) => {
146 let (prob, new_state) = self.project_onto_outcome(state, outcome)?;
148 Ok(OperationResult::Probabilistic {
149 outcome,
150 probability: prob,
151 state: new_state,
152 })
153 }
154 None => {
155 let probabilities = self.get_probabilities(state)?;
157 let mut outcomes = Vec::new();
158
159 for (outcome, &prob) in probabilities.iter().enumerate() {
160 if prob > 1e-10 {
161 let (_, new_state) = self.project_onto_outcome(state, outcome)?;
162 outcomes.push(MeasurementOutcome {
163 outcome,
164 probability: prob,
165 state: new_state,
166 });
167 }
168 }
169
170 Ok(OperationResult::MultiOutcome(outcomes))
171 }
172 }
173 }
174
175 fn apply_to_density_matrix(
176 &self,
177 rho: &ArrayView2<Complex64>,
178 ) -> QuantRS2Result<Array2<Complex64>> {
179 let n_qubits = (rho.nrows() as f64).log2() as usize;
180 let mut result = Array2::zeros(rho.raw_dim());
181
182 for outcome in 0..(1 << self.qubits.len()) {
184 let projector = self.create_projector(outcome, n_qubits)?;
185 let proj_rho = projector.dot(rho).dot(&projector);
186 result = result + proj_rho;
187 }
188
189 Ok(result)
190 }
191
192 fn qubits(&self) -> Vec<QubitId> {
193 self.qubits.clone()
194 }
195
196 fn is_deterministic(&self) -> bool {
197 false
198 }
199}
200
201impl ProjectiveMeasurement {
202 fn create_projector(
204 &self,
205 outcome: usize,
206 total_qubits: usize,
207 ) -> QuantRS2Result<Array2<Complex64>> {
208 let dim = 1 << total_qubits;
209 let mut projector = Array2::zeros((dim, dim));
210
211 for idx in 0..dim {
212 if self.extract_outcome_from_index(idx, total_qubits) == outcome {
213 projector[[idx, idx]] = Complex64::new(1.0, 0.0);
214 }
215 }
216
217 Ok(projector)
218 }
219}
220
221#[derive(Debug, Clone)]
223pub struct POVMMeasurement {
224 pub elements: Vec<Array2<Complex64>>,
226 pub qubits: Vec<QubitId>,
228}
229
230impl POVMMeasurement {
231 pub fn new(elements: Vec<Array2<Complex64>>, qubits: Vec<QubitId>) -> QuantRS2Result<Self> {
233 let dim = elements[0].nrows();
235 let mut sum = Array2::<Complex64>::zeros((dim, dim));
236
237 for element in &elements {
238 if element.nrows() != dim || element.ncols() != dim {
239 return Err(QuantRS2Error::InvalidInput(
240 "All POVM elements must have the same dimension".to_string(),
241 ));
242 }
243 sum = sum + element;
244 }
245
246 for i in 0..dim {
248 for j in 0..dim {
249 let expected = if i == j {
250 Complex64::new(1.0, 0.0)
251 } else {
252 Complex64::new(0.0, 0.0)
253 };
254 let diff: Complex64 = sum[[i, j]] - expected;
255 if diff.norm_sqr().sqrt() > 1e-10 {
256 return Err(QuantRS2Error::InvalidInput(
257 "POVM elements do not sum to identity".to_string(),
258 ));
259 }
260 }
261 }
262
263 Ok(Self { elements, qubits })
264 }
265
266 pub fn get_probabilities(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<Vec<f64>> {
268 let mut probabilities = Vec::new();
269
270 for element in &self.elements {
271 let temp = element.dot(state);
273 let prob = state
274 .iter()
275 .zip(temp.iter())
276 .map(|(&psi, &m_psi)| psi.conj() * m_psi)
277 .sum::<Complex64>()
278 .re;
279 probabilities.push(prob);
280 }
281
282 Ok(probabilities)
283 }
284}
285
286impl QuantumOperation for POVMMeasurement {
287 fn apply_to_state(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<OperationResult> {
288 let probabilities = self.get_probabilities(state)?;
289 let mut outcomes = Vec::new();
290
291 for (i, (&prob, element)) in probabilities.iter().zip(&self.elements).enumerate() {
292 if prob > 1e-10 {
293 let new_state = element.dot(state) / prob.sqrt();
295 outcomes.push(MeasurementOutcome {
296 outcome: i,
297 probability: prob,
298 state: new_state,
299 });
300 }
301 }
302
303 Ok(OperationResult::MultiOutcome(outcomes))
304 }
305
306 fn apply_to_density_matrix(
307 &self,
308 rho: &ArrayView2<Complex64>,
309 ) -> QuantRS2Result<Array2<Complex64>> {
310 let mut result = Array2::zeros(rho.raw_dim());
311
312 for element in &self.elements {
314 let m_dag = element.t().mapv(|x| x.conj());
315 let term = element.dot(rho).dot(&m_dag);
316 result = result + term;
317 }
318
319 Ok(result)
320 }
321
322 fn qubits(&self) -> Vec<QubitId> {
323 self.qubits.clone()
324 }
325
326 fn is_deterministic(&self) -> bool {
327 false
328 }
329}
330
331#[derive(Debug, Clone)]
333pub struct Reset {
334 pub qubits: Vec<QubitId>,
336}
337
338impl Reset {
339 pub fn new(qubits: Vec<QubitId>) -> Self {
341 Self { qubits }
342 }
343}
344
345impl QuantumOperation for Reset {
346 fn apply_to_state(&self, state: &ArrayView1<Complex64>) -> QuantRS2Result<OperationResult> {
347 let n_qubits = (state.len() as f64).log2() as usize;
348 let mut new_state = Array1::zeros(state.len());
349
350 let mut norm_squared = 0.0;
352 for (idx, &) in state.iter().enumerate() {
353 let mut should_keep = true;
354 for &qubit in &self.qubits {
355 let bit = (idx >> (n_qubits - 1 - qubit.0 as usize)) & 1;
356 if bit != 0 {
357 should_keep = false;
358 break;
359 }
360 }
361 if should_keep {
362 new_state[idx] = amp;
363 norm_squared += amp.norm_sqr();
364 }
365 }
366
367 if norm_squared < 1e-10 {
368 new_state[0] = Complex64::new(1.0, 0.0);
370 } else {
371 new_state = new_state / norm_squared.sqrt();
372 }
373
374 Ok(OperationResult::Deterministic(new_state))
375 }
376
377 fn apply_to_density_matrix(
378 &self,
379 rho: &ArrayView2<Complex64>,
380 ) -> QuantRS2Result<Array2<Complex64>> {
381 let n_qubits = (rho.nrows() as f64).log2() as usize;
384 let mut result = Array2::zeros(rho.raw_dim());
385
386 for i in 0..rho.nrows() {
388 for j in 0..rho.ncols() {
389 let mut should_keep = true;
390 for &qubit in &self.qubits {
391 let bit_i = (i >> (n_qubits - 1 - qubit.0 as usize)) & 1;
392 let bit_j = (j >> (n_qubits - 1 - qubit.0 as usize)) & 1;
393 if bit_i != 0 || bit_j != 0 {
394 should_keep = false;
395 break;
396 }
397 }
398 if should_keep {
399 result[[i, j]] = rho[[i, j]];
400 }
401 }
402 }
403
404 let trace = (0..result.nrows()).map(|i| result[[i, i]].re).sum::<f64>();
406 if trace > 1e-10 {
407 result = result / trace;
408 }
409
410 Ok(result)
411 }
412
413 fn qubits(&self) -> Vec<QubitId> {
414 self.qubits.clone()
415 }
416
417 fn is_deterministic(&self) -> bool {
418 true
419 }
420}
421
422pub fn sample_outcome(probabilities: &[f64]) -> QuantRS2Result<usize> {
424 let mut rng = thread_rng();
425 let r: f64 = rng.gen();
426
427 let mut cumsum = 0.0;
428 for (i, &prob) in probabilities.iter().enumerate() {
429 cumsum += prob;
430 if r < cumsum {
431 return Ok(i);
432 }
433 }
434
435 Err(QuantRS2Error::ComputationError(
437 "Probabilities do not sum to 1".to_string(),
438 ))
439}
440
441pub fn apply_and_sample<O: QuantumOperation>(
443 operation: &O,
444 state: &ArrayView1<Complex64>,
445) -> QuantRS2Result<(usize, Array1<Complex64>)> {
446 match operation.apply_to_state(state)? {
447 OperationResult::Deterministic(new_state) => Ok((0, new_state)),
448 OperationResult::Probabilistic { outcome, state, .. } => Ok((outcome, state)),
449 OperationResult::MultiOutcome(outcomes) => {
450 let probabilities: Vec<f64> = outcomes.iter().map(|o| o.probability).collect();
451 let sampled_idx = sample_outcome(&probabilities)?;
452 let outcome = &outcomes[sampled_idx];
453 Ok((outcome.outcome, outcome.state.clone()))
454 }
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 use super::*;
461
462 #[test]
463 fn test_projective_measurement() {
464 let state = Array1::from_vec(vec![
466 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
467 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
468 ]);
469
470 let measurement = ProjectiveMeasurement::new(vec![QubitId(0)]);
471 let probs = measurement.get_probabilities(&state.view()).unwrap();
472
473 assert!((probs[0] - 0.5).abs() < 1e-10);
474 assert!((probs[1] - 0.5).abs() < 1e-10);
475 }
476
477 #[test]
478 fn test_reset_operation() {
479 let state = Array1::from_vec(vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)]);
481
482 let reset = Reset::new(vec![QubitId(0)]);
483 let result = reset.apply_to_state(&state.view()).unwrap();
484
485 match result {
486 OperationResult::Deterministic(new_state) => {
487 assert!((new_state[0].norm() - 1.0).abs() < 1e-10);
488 assert!(new_state[1].norm() < 1e-10);
489 }
490 _ => panic!("Reset should be deterministic"),
491 }
492 }
493}