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