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