microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/paulimer/src/outcome_specific_simulation.rs

316lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use rand::{thread_rng, Rng};
5
6use crate::{
7 bits::{Bitwise, IndexSet},
8 clifford::{
9 Clifford, CliffordMutable, CliffordUnitary, ControlledPauli, Hadamard, PauliExponent, Swap,
10 },
11 pauli::{anti_commutes_with, generic::PhaseExponent, Pauli, PauliBits, PauliUnitary, Phase},
12 quantum_core, Simulation, UnitaryOp,
13};
14
15type SparsePauli = PauliUnitary<IndexSet, u8>;
16
17#[must_use]
18pub struct OutcomeSpecificSimulation {
19 clifford: CliffordUnitary, // R
20 outcome_vector: Vec<bool>,
21 random_outcome_indicator: Vec<bool>, // vec(p), [j] is true iff vec(p)_j = 1/2
22 num_random_bits: usize,
23 use_all_zeros: bool,
24}
25
26impl OutcomeSpecificSimulation {
27 pub fn new(num_qubits: usize, num_outcomes: usize) -> Self {
28 OutcomeSpecificSimulation {
29 clifford: CliffordUnitary::identity(num_qubits),
30 outcome_vector: Vec::<bool>::with_capacity(num_outcomes),
31 random_outcome_indicator: Vec::<bool>::with_capacity(num_outcomes),
32 num_random_bits: 0,
33 use_all_zeros: false,
34 }
35 }
36
37 pub fn new_with_random_outcomes(num_qubits: usize, num_outcomes: usize) -> Self {
38 Self::new(num_qubits, num_outcomes)
39 }
40
41 pub fn new_with_zero_outcomes(num_qubits: usize, num_outcomes: usize) -> Self {
42 let mut result = Self::new(num_qubits, num_outcomes);
43 result.use_all_zeros = true;
44 result
45 }
46}
47
48pub fn new_outcome_specific_simulation(
49 num_qubits: usize,
50 num_outcomes: usize,
51) -> OutcomeSpecificSimulation {
52 OutcomeSpecificSimulation::new_with_random_outcomes(num_qubits, num_outcomes)
53}
54
55impl OutcomeSpecificSimulation {
56 pub fn clifford(&self) -> &CliffordUnitary {
57 &self.clifford
58 }
59
60 #[must_use]
61 pub fn outcome_vector(&self) -> &Vec<bool> {
62 &self.outcome_vector
63 }
64}
65
66pub fn apply_hadamard(simulation: &mut OutcomeSpecificSimulation, qubit_index: usize) {
67 Hadamard(qubit_index) * &mut simulation.clifford;
68}
69
70pub fn apply_cx(simulation: &mut OutcomeSpecificSimulation, control_id: usize, target_id: usize) {
71 let control = PauliUnitary::from_bits(IndexSet::new(), IndexSet::from_iter([control_id]), 0u8);
72 let target = PauliUnitary::from_bits(IndexSet::from_iter([target_id]), IndexSet::new(), 0u8);
73 ControlledPauli::new(control, target) * &mut simulation.clifford;
74}
75
76pub fn apply_cz(simulation: &mut OutcomeSpecificSimulation, control_id: usize, target_id: usize) {
77 let control = PauliUnitary::from_bits(IndexSet::new(), IndexSet::from_iter([control_id]), 0u8);
78 let target = PauliUnitary::from_bits(IndexSet::new(), IndexSet::from_iter([target_id]), 0u8);
79 ControlledPauli::new(control, target) * &mut simulation.clifford;
80}
81
82pub fn apply_pauli<Bits: PauliBits, Phase: PhaseExponent>(
83 simulation: &mut OutcomeSpecificSimulation,
84 pauli: &PauliUnitary<Bits, Phase>,
85) {
86 pauli * &mut simulation.clifford;
87}
88
89pub fn apply_pauli_exponent<Bits: PauliBits, Phase: PhaseExponent>(
90 simulation: &mut OutcomeSpecificSimulation,
91 pauli: PauliUnitary<Bits, Phase>,
92) {
93 // simulation.clifford = PauliExponent(pauli) * simulation.clifford;
94 // clifford = PauliExponent(Pauli) * clifford;
95 PauliExponent::new(pauli) * &mut simulation.clifford;
96}
97
98pub fn apply_controlled_pauli<Bits: PauliBits, Phase: PhaseExponent>(
99 simulation: &mut OutcomeSpecificSimulation,
100 control: PauliUnitary<Bits, Phase>,
101 target: PauliUnitary<Bits, Phase>,
102) {
103 ControlledPauli::new(control, target) * &mut simulation.clifford;
104}
105
106pub fn apply_swap(simulation: &mut OutcomeSpecificSimulation, qubit_id1: usize, qubit_id2: usize) {
107 Swap(qubit_id1, qubit_id2) * &mut simulation.clifford;
108}
109
110/// # Panics
111/// Panics if `hint` commutes with `observable`
112pub fn measure_pauli_with_hint<HintBits: PauliBits, HintPhase: PhaseExponent>(
113 simulation: &mut OutcomeSpecificSimulation,
114 observable: &SparsePauli,
115 hint: &PauliUnitary<HintBits, HintPhase>,
116) {
117 assert!(
118 anti_commutes_with(observable, hint),
119 "observable={observable}, hint={hint}"
120 );
121 let preimage = simulation.clifford.preimage(hint);
122
123 if preimage.x_bits().support().next().is_some() {
124 // hint is not true
125 measure_pauli(simulation, observable);
126 } else {
127 let mut pauli = observable.clone() * hint;
128 pauli *= Phase::from_exponent(3u8.wrapping_sub(preimage.xz_phase_exponent().raw_value()));
129 PauliExponent::new(pauli) * &mut simulation.clifford;
130 allocate_random_bit(simulation);
131 apply_conditional_pauli(
132 simulation,
133 hint,
134 &[simulation.outcome_vector.len() - 1],
135 true,
136 );
137 }
138}
139
140pub fn allocate_random_bit(simulation: &mut OutcomeSpecificSimulation) {
141 simulation.outcome_vector.push(if simulation.use_all_zeros {
142 false
143 } else {
144 thread_rng().gen()
145 });
146 simulation.random_outcome_indicator.push(true);
147 simulation.num_random_bits += 1;
148}
149
150pub fn measure_pauli(simulation: &mut OutcomeSpecificSimulation, observable: &SparsePauli) {
151 let preimage = simulation.clifford.preimage(observable);
152 let non_zero_pos = preimage.x_bits().support().next();
153 match non_zero_pos {
154 Some(pos) => {
155 let hint = simulation.clifford.image_z(pos);
156 measure_pauli_with_hint(simulation, observable, &hint);
157 }
158 None => {
159 measure_deterministic(simulation, &preimage);
160 }
161 }
162}
163
164fn measure_deterministic<Bits: PauliBits, Phase: PhaseExponent>(
165 simulation: &mut OutcomeSpecificSimulation,
166 preimage: &PauliUnitary<Bits, Phase>,
167) {
168 debug_assert!(preimage.xz_phase_exponent().is_even());
169 simulation
170 .outcome_vector
171 .push(preimage.xz_phase_exponent().value() == 2);
172 simulation.random_outcome_indicator.push(false);
173}
174
175fn is_stabilizer<Bits: PauliBits, Phase: PhaseExponent>(
176 simulation: &OutcomeSpecificSimulation,
177 pauli: &PauliUnitary<Bits, Phase>,
178) -> bool {
179 let preimage = simulation.clifford.preimage(pauli);
180 preimage.x_bits().weight() == 0 && preimage.xz_phase_exponent().value() == 0
181}
182
183fn is_stabilizer_up_to_sign<Bits: PauliBits, Phase: PhaseExponent>(
184 simulation: &OutcomeSpecificSimulation,
185 pauli: &PauliUnitary<Bits, Phase>,
186) -> bool {
187 let preimage = simulation.clifford.preimage(pauli);
188 preimage.x_bits().weight() == 0
189}
190
191pub fn apply_conditional_pauli<Bits: PauliBits, Phase: PhaseExponent>(
192 simulation: &mut OutcomeSpecificSimulation,
193 pauli: &PauliUnitary<Bits, Phase>,
194 outcomes_indicator: &[usize],
195 parity: bool,
196) {
197 if total_parity(simulation.outcome_vector(), outcomes_indicator) == parity {
198 apply_pauli(simulation, pauli);
199 }
200}
201
202fn total_parity(outcome_vector: &[bool], outcomes_indicator: &[usize]) -> bool {
203 let mut res = false;
204 for j in outcomes_indicator {
205 res ^= outcome_vector[*j];
206 }
207 res
208}
209
210#[test]
211fn init_test() {
212 let mut _outcome_specific_simulation = new_outcome_specific_simulation(2, 10);
213 // println!("{:?}",outcome_specific_simulation.random_outcome_source())
214}
215
216impl Simulation for OutcomeSpecificSimulation {
217 fn pauli_exp(&mut self, observable: &[quantum_core::PositionedPauliObservable]) {
218 let pauli = SparsePauli::from(observable);
219 apply_pauli_exponent(self, pauli);
220 }
221
222 fn controlled_pauli(
223 &mut self,
224 observable1: &[quantum_core::PositionedPauliObservable],
225 observable2: &[quantum_core::PositionedPauliObservable],
226 ) {
227 let pauli1 = SparsePauli::from(observable1);
228 let pauli2 = SparsePauli::from(observable2);
229 apply_controlled_pauli(self, pauli1, pauli2);
230 }
231
232 fn pauli(&mut self, observable: &[quantum_core::PositionedPauliObservable]) {
233 let pauli = SparsePauli::from(observable);
234 apply_pauli(self, &pauli);
235 }
236
237 fn measure(&mut self, observable: &[quantum_core::PositionedPauliObservable]) -> usize {
238 let pauli = SparsePauli::from(observable);
239 measure_pauli(self, &pauli);
240 self.outcome_vector().len() - 1
241 }
242
243 fn measure_sparse(&mut self, observable: &SparsePauli) -> usize {
244 measure_pauli(self, observable);
245 self.outcome_vector().len() - 1
246 }
247
248 fn measure_with_hint(
249 &mut self,
250 observable: &[quantum_core::PositionedPauliObservable],
251 hint: &[quantum_core::PositionedPauliObservable],
252 ) -> usize {
253 let pauli = SparsePauli::from(observable);
254 let hint = SparsePauli::from(hint);
255 measure_pauli_with_hint(self, &pauli, &hint);
256 self.outcome_vector().len() - 1
257 }
258
259 fn assert_stabilizer(&self, observable: &[quantum_core::PositionedPauliObservable]) {
260 let sparse_pauli = SparsePauli::from(observable);
261 assert!(is_stabilizer(self, &sparse_pauli));
262 }
263
264 fn assert_stabilizer_up_to_sign(&self, observable: &[quantum_core::PositionedPauliObservable]) {
265 let sparse_pauli = SparsePauli::from(observable);
266 assert!(is_stabilizer_up_to_sign(self, &sparse_pauli));
267 }
268
269 fn assert_anti_stabilizer(&self, observable: &[quantum_core::PositionedPauliObservable]) {
270 let sparse_pauli = SparsePauli::from(observable);
271 assert!(!is_stabilizer_up_to_sign(self, &sparse_pauli));
272 }
273
274 fn with_capacity(num_qubits: usize, num_outcomes: usize, _num_random_outcomes: usize) -> Self {
275 OutcomeSpecificSimulation::new_with_random_outcomes(num_qubits, num_outcomes)
276 }
277
278 fn new() -> Self {
279 Self::with_capacity(1, 1, 1)
280 }
281
282 fn conditional_pauli(
283 &mut self,
284 observable: &[quantum_core::PositionedPauliObservable],
285 outcomes: &[usize],
286 parity: bool,
287 ) {
288 let pauli = SparsePauli::from(observable);
289 apply_conditional_pauli(self, &pauli, outcomes, parity);
290 }
291
292 fn random_bit(&mut self) -> usize {
293 allocate_random_bit(self);
294 self.num_random_bits - 1
295 }
296
297 fn num_random_outcomes(&self) -> usize {
298 self.num_random_bits
299 }
300
301 fn random_outcome_indicator(&self) -> &[bool] {
302 &self.random_outcome_indicator
303 }
304
305 fn apply_unitary(&mut self, unitary_op: UnitaryOp, support: &[usize]) {
306 self.clifford.left_mul(unitary_op, support);
307 }
308
309 fn apply_clifford(&mut self, clifford: &CliffordUnitary, support: &[usize]) {
310 self.clifford.left_mul_clifford(clifford, support);
311 }
312
313 fn apply_permutation(&mut self, permutation: &[usize], support: &[usize]) {
314 self.clifford.left_mul_permutation(permutation, support);
315 }
316}
317