Qit/
core.rs

1/*!
2Qubit struct used for simulation and complex number struct that composes it.
3
4In this library, qubits are represented by vectors of complex numbers, and gates are defined as structs that change vectors of complex numbers.
5"gates" has basic 1-bit operators such as H, Z, Y, and Z, 2-bit operators such as CX, and a struct that uses them all as one operator.
6
7# Example usage
8```
9use Qit::{circuits::wrapping_qadd_const, core::{Qubits, Applicable}};
10let q_in = Qubits::from_num(3, 4);
11// q_in = |0100⟩
12let add = wrapping_qadd_const(&vec![0, 1, 2], 3);
13// add * |b⟩ = |b + 3⟩
14let q_out = add.apply(q_in);
15q_out.print_cmps();
16// |000⟩ : +0.000 +0.000i
17// |001⟩ : +0.000 +0.000i
18// |010⟩ : +0.000 +0.000i
19// |011⟩ : +0.000 +0.000i
20// |100⟩ : +0.000 +0.000i
21// |101⟩ : +0.000 +0.000i
22// |110⟩ : +0.000 +0.000i
23// |111⟩ : +1.000 +0.000i
24```
25*/
26
27use std::fmt;
28use std::ops;
29
30/**
31 Complex numbers implemented with functions required for quantum simulation
32 It is implemented with the only purpose of expressing quantum bits.
33
34 # Example usage
35 ```
36use Qit::core::Comp;
37let zero = Comp::zero();
38println!("{}", zero);
39// +0.000 +0.000i
40let re: f64 = 1.0;
41let im: f64 = -30.0;
42let c = Comp::new(re, im);
43println!("{}", c);
44// +0.200 -30.000i
45let c1 = Comp::new(2.0, 1.0);
46let c2 = Comp::new(1.0, 2.0);
47let add_c1c2 = c1 + c2;
48assert_eq!(add_c1c2, Comp::new(3.0, 3.0));
49
50let sub_c1c2 = c1 - c2;
51assert_eq!(sub_c1c2, Comp::new(1.0, -1.0));
52
53let mul_c1c2 = c1 * c2;
54assert_eq!(mul_c1c2, Comp::new(0.0, 5.0));
55
56let f1: f64 = 2.0;
57let add_c1f1 = c1 + f1;
58assert_eq!(add_c1f1, Comp::new(4.0, 1.0));
59
60let sub_c1f1 = c1 - f1;
61assert_eq!(sub_c1f1, Comp::new(0.0, 1.0));
62
63let mul_c1f1 = c1 * f1;
64assert_eq!(mul_c1f1, Comp::new(4.0, 2.0));
65 ```
66 */
67#[derive(PartialEq, Debug, Clone, Copy)]
68pub struct Comp(pub f64, pub f64);
69
70// pub mod circuits;
71// pub mod gates;
72pub mod mod_funcs;
73
74impl Comp {
75    pub fn new(re: f64, im: f64) -> Self {
76        return Comp(re, im);
77    }
78
79    pub fn abs_square(&self) -> f64 {
80        return self.0 * self.0 + self.1 * self.1;
81    }
82
83    pub fn zero() -> Self {
84        return Comp(0.0, 0.0);
85    }
86}
87
88impl fmt::Display for Comp {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(
91            f,
92            "{:+.3} {:+.3}i",
93            (self.0 * 1000.0).round() / 1000.0,
94            (self.1 * 1000.0).round() / 1000.0
95        )
96    }
97}
98
99impl ops::Add<Comp> for Comp {
100    type Output = Comp;
101    fn add(self, _rhs: Comp) -> Comp {
102        return Comp(self.0 + _rhs.0, self.1 + _rhs.1);
103    }
104}
105
106impl ops::Add<f64> for Comp {
107    type Output = Comp;
108    fn add(self, rhs: f64) -> Comp {
109        return Comp(self.0 + rhs, self.1);
110    }
111}
112
113impl ops::Sub<Comp> for Comp {
114    type Output = Comp;
115    fn sub(self, _rhs: Comp) -> Comp {
116        return Comp(self.0 - _rhs.0, self.1 - _rhs.1);
117    }
118}
119
120impl ops::Sub<f64> for Comp {
121    type Output = Comp;
122    fn sub(self, rhs: f64) -> Comp {
123        return Comp(self.0 - rhs, self.1);
124    }
125}
126
127impl ops::Mul<Comp> for Comp {
128    type Output = Comp;
129    fn mul(self, _rhs: Comp) -> Comp {
130        return Comp(
131            self.0 * _rhs.0 - self.1 * _rhs.1,
132            self.0 * _rhs.1 + self.1 * _rhs.0,
133        );
134    }
135}
136
137impl ops::Mul<f64> for Comp {
138    type Output = Comp;
139    fn mul(self, rhs: f64) -> Comp {
140        return Comp(self.0 * rhs, self.1 * rhs);
141    }
142}
143
144/**
145struct representing a collection of pseudo-qubits.
146
147# Example usage
148```
149use Qit::core::{pop_from_probs, Qubits};
150let qbit = Qubits::from_num(2, 1);
151qbit.print_probs();
152// |00⟩ :   0%
153// |01⟩ : 100%
154// |10⟩ :   0%
155// |11⟩ :   0%
156qbit.print_cmps();
157// |00⟩ : +0.000 +0.000i
158// |01⟩ : +1.000 +0.000i
159// |10⟩ : +0.000 +0.000i
160// |11⟩ : +0.000 +0.000i
161let probs = qbit._measure(&vec![0, 1]);
162println!("{probs:#?}");
163// [
164//     0.0,
165//     1.0,
166//     0.0,
167//     0.0,
168// ]
169println!("{}", pop_from_probs(&probs, 2));
170// 1
171```
172 */
173#[derive(Clone)]
174pub struct Qubits {
175    pub size: usize,
176    pub bits: Vec<Comp>,
177}
178
179impl Qubits {
180    /**
181    A function that creates a qubit that represents the input value
182
183    1.0 * |number⟩
184    */
185    pub fn from_num(size: usize, number: usize) -> Self {
186        assert!((1 << size) > number);
187        let mut bits = vec![Comp::zero(); 1 << size];
188        bits[number] = Comp(1.0, 0.0);
189        return Qubits {
190            size: size,
191            bits: bits,
192        };
193    }
194
195    pub fn from_comp(size: usize, number: usize, comp: Comp) -> Self {
196        assert!((1 << size) > number);
197        assert!(comp.abs_square() == 1.0);
198        let mut bits = vec![Comp::zero(); 1 << size];
199        bits[number] = comp;
200        return Qubits {
201            size: size,
202            bits: bits,
203        };
204    }
205
206    pub fn from_bits(size: usize, bits: Vec<Comp>) -> Self {
207        assert_eq!(1 << size, bits.len());
208        return Qubits {
209            size: size,
210            bits: bits,
211        };
212    }
213
214    /**
215     * Output |0...0⟩ Qubit of input size
216     */
217    pub fn zeros(size: usize) -> Self {
218        let mut bits = vec![Comp::zero(); 1 << size];
219        bits[0] = Comp(1.0, 0.0);
220        return Qubits {
221            size: size,
222            bits: bits,
223        };
224    }
225
226    /**
227     * Output the probability of outputting each bit string as a vector
228     */
229    pub fn print_probs(&self) {
230        for index in 0..(1 << self.size) {
231            println!(
232                "|{index:0>size$b}⟩ : {prob:>3}%",
233                index = index,
234                size = self.size,
235                prob = (self.bits[index].abs_square() * 100.0).round()
236            );
237        }
238    }
239
240    /**
241     * Output all bit strings and their corresponding complex numbers
242     */
243    pub fn print_cmps(&self) {
244        for index in 0..(1 << self.size) {
245            println!(
246                "|{index:0>size$b}⟩ : {cmp}",
247                index = index,
248                size = self.size,
249                cmp = self.bits[index]
250            );
251        }
252    }
253
254    pub fn probs(&self) -> Vec<f64> {
255        let mut prob = vec![0.0; (1 << self.size)];
256        for index in 0..(1 << self.size) {
257            prob[index] = self.bits[index].abs_square();
258        }
259        return prob;
260    }
261
262    /**
263     * Function to obtain the most probable qubit string
264     */
265    pub fn pop_most_plausible(&self) -> usize {
266        let mut max_prob = 0.0;
267        let mut max_idx = 0;
268        for i in 0..(1 << self.size) {
269            let prob = self.bits[i].abs_square();
270            if max_prob < prob {
271                max_prob = prob;
272                max_idx = i;
273            }
274        }
275        return max_idx;
276    }
277
278    /**
279    Function to obtain probability distribution of qubits
280     */
281    pub fn _measure(&self, tar: &[usize]) -> Vec<f64> {
282        let mut probs: Vec<f64> = Vec::new();
283        for _ in 0..(1 << tar.len()) {
284            probs.push(0.0);
285        }
286        for i in 0..(1 << self.size) {
287            let mut tar_idx = 0;
288            for j in 0..tar.len() {
289                tar_idx |= (1 & (i >> tar[j])) << j;
290            }
291            probs[tar_idx] += self.bits[i].abs_square();
292        }
293
294        return probs;
295    }
296
297    pub fn _print_measure(&self, tar: &[usize]) {
298        let mut probs: Vec<f64> = Vec::new();
299        for _ in 0..(1 << tar.len()) {
300            probs.push(0.0);
301        }
302        for i in 0..(1 << self.size) {
303            let mut tar_idx = 0;
304            for j in 0..tar.len() {
305                tar_idx |= (1 & (i >> tar[j])) << j;
306            }
307            probs[tar_idx] += self.bits[i].abs_square();
308        }
309
310        for index in 0..(1 << tar.len()) {
311            println!(
312                "|{index:0>size$b}⟩ : {prob:>3.2}%",
313                index = index,
314                size = tar.len(),
315                prob = (probs[index] * 10000.0).round() / 100.0
316            );
317        }
318    }
319}
320
321/**
322 Minimum traits that gates that manipulate qubits must satisfy
323*/
324pub trait Applicable {
325    fn apply(&self, qubits: Qubits) -> Qubits {
326        let it = BitSlideIndex::new(1 << qubits.size, 0);
327        return self.apply_iter(qubits, &it);
328    }
329    fn name(&self) -> String;
330    fn apply_iter(&self, qubits: Qubits, iter: &BitSlideIndex) -> Qubits;
331}
332
333/**
334struct used internally when applying gates
335 */
336pub struct BitSlideIndex {
337    idx: usize,
338    pub mask: usize,
339    to: usize,
340}
341
342impl BitSlideIndex {
343    pub fn new(to: usize, mask: usize) -> Self {
344        return BitSlideIndex {
345            idx: 0,
346            mask: mask,
347            to: to,
348        };
349    }
350
351    pub fn merge(&self, other: usize) -> Self {
352        if self.mask & other > 0 {
353            println!("self.mask:{:b}, other_mask:{:b}", self.mask, other);
354            panic!("invalid mask was input.");
355        }
356        return BitSlideIndex {
357            idx: 0,
358            mask: self.mask | other,
359            to: self.to,
360        };
361    }
362
363    pub fn init(&mut self) {
364        self.idx = 0;
365    }
366}
367
368impl Iterator for BitSlideIndex {
369    type Item = usize;
370
371    fn next(&mut self) -> Option<Self::Item> {
372        let idx = self.idx;
373        if (idx & self.mask) != self.mask {
374            let idx = idx | self.mask;
375            self.idx = idx + 1;
376            if idx < self.to {
377                return Some(idx);
378            }
379        } else {
380            self.idx = idx + 1;
381            if idx < self.to {
382                return Some(idx);
383            }
384        }
385        return None;
386    }
387}
388
389/**
390Trait that implements make gates inversed
391 */
392pub trait Inversible {
393    fn inverse(&mut self) {}
394}
395
396/**
397A trait that combines the Applicable and Inversible traits.
398 */
399pub trait Operator: Applicable + Inversible {}
400
401/**
402Obtain the observed bit string from the probability distribution extracted from the measure function
403 */
404pub fn pop_from_probs(probs: &[f64], size: usize) -> usize {
405    use rand::prelude::*;
406
407    loop {
408        let mut r: f64 = rand::thread_rng().gen();
409        for i in 0..(1 << size) {
410            r -= probs[i];
411            if r < 0.0 {
412                return i;
413            }
414        }
415    }
416}