1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
use crate::quantum::types::{
quantum_gate::QuantumGate, quantum_operators::QuantumOperator,
quantum_position::QuantumPosition, qubit::Qubit,
};
use num_complex::Complex;
use rand::Rng;
use std::{fmt, ops};
impl Qubit {
/// [`Qubit::new`] will create a new [`Qubit`] with a [`QuantumPosition`]
/// in complex vector space.
///
/// # Example
/// [`Qubit::new`] can be used to create a new [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_position::QuantumPosition};
///
/// fn create_qubit() -> Qubit {
/// Qubit::new(QuantumPosition::ZERO)
/// }
/// ```
pub fn new(position: QuantumPosition) -> Self {
// The amplitudes of the qubit's initial and possible magnitude must equal 1,
// or the qubit has an invalid position.
assert!(position.has_valid_amplitude(), "Invalid qubit positions");
Qubit {
positions: vec![position],
}
}
/// [`Qubit::zero`] will return a new [`Qubit`] with it's position set to [`QuantumPosition::ZERO`].
///
/// [`Qubit::zero`] can be represented by the following matrix:
/// $$ 0 = \begin{pmatrix}1 \\\ 0 \end{pmatrix} $$
///
/// # Example
/// [`Qubit::zero`] can be used to get a zero [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_position::QuantumPosition};
///
/// fn get_zero_qubit() -> Qubit {
/// Qubit::zero()
/// }
/// ```
pub fn zero() -> Self {
Qubit::new(QuantumPosition::ZERO)
}
/// [`Qubit::one`] (also referred to as an "identity qubit") will return a new [`Qubit`] with it's
/// position set to [`QuantumPosition::ONE`].
///
/// [`Qubit::one`] can be represented by the following matrix:
/// $$ 1 = \begin{pmatrix} 0 \\\ 1 \end{pmatrix} $$
///
/// # Example
/// [`Qubit::one`] can be used to get an identity [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_position::QuantumPosition};
///
/// fn get_identity_qubit() -> Qubit {
/// Qubit::one()
/// }
/// ```
pub fn one() -> Self {
Qubit::new(QuantumPosition::ONE)
}
/// [`Qubit::flip`] will return a new [`Qubit`] with it's position set to [`QuantumPosition::FLIP`].
///
/// [`Qubit::flip`] can be represented by the following matrix:
/// $$ F = \begin{pmatrix} 0 \\\ -1 \end{pmatrix} $$
///
/// # Example
/// [`Qubit::flip`] can be used to get a flipped [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_position::QuantumPosition};
///
/// fn get_flipped_qubit() -> Qubit {
/// Qubit::flip()
/// }
/// ```
pub fn flip() -> Self {
Qubit::new(QuantumPosition::FLIP)
}
/// [`Qubit::quarter_turn`] will return a new [`Qubit`] with it's position set to
/// [`QuantumPosition::QUARTER_TURN`].
///
/// [`Qubit::quarter_turn`] can be represented by the following matrix:
/// $$ Qt = \begin{pmatrix} i \\\ 0 \end{pmatrix} $$
///
/// # Example
/// [`Qubit::quarter_turn`] can be used to get a quarter-turned [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_position::QuantumPosition};
///
/// fn get_quarter_turned_qubit() -> Qubit {
/// Qubit::quarter_turn()
/// }
/// ```
pub fn quarter_turn() -> Self {
Qubit::new(QuantumPosition::QUARTER_TURN)
}
/// [`Qubit::back_quarter_turn`] will return a new [`Qubit`] with it's position set to
/// [`QuantumPosition::BACK_QUARTER_TURN`].
///
/// [`Qubit::back_quarter_turn`] can be represented by the following matrix:
/// $$ B(Qt) = \begin{pmatrix} 0 \\\ -i \end{pmatrix} $$
///
/// # Example
/// [`Qubit::back_quarter_turn`] can be used to get a backwards quarter-turned [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_position::QuantumPosition};
///
/// fn get_backwards_quarter_turned_qubit() -> Qubit {
/// Qubit::back_quarter_turn()
/// }
/// ```
pub fn back_quarter_turn() -> Self {
Qubit::new(QuantumPosition::BACK_QUARTER_TURN)
}
/// [`Qubit::update`] will move the [`Qubit`] that calls it to a new [`QuantumPosition`] in
/// complex vector space, and maintains the old position inside of [`Qubit::positions`].
///
/// # Example
/// [`Qubit::update`] can be used to update the position of a [`Qubit`].
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_position::QuantumPosition};
///
/// fn move_qubit(mut qubit: Qubit, new_position: QuantumPosition) {
/// qubit.update(new_position);
/// }
/// ```
pub fn update(&mut self, new_position: QuantumPosition) {
self.positions.push(new_position);
}
/// [`Qubit::apply_gate`] will apply a [`QuantumGate`] to the [`QuantumPosition`]
/// of the [`Qubit`] that calls it, then return the modified [`Qubit`].
///
/// It is how a [`Qubit`] has logic applied to it.
///
/// # Example
/// [`Qubit::apply_gate`] can be used to apply a NOT gate to a [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_gate::QuantumGate};
///
/// fn invert_qubit_amplitudes(qubit: Qubit) -> Qubit {
/// qubit.apply_gate(&QuantumGate::NOT)
/// }
/// ```
pub fn apply_gate(&self, gate: &QuantumGate) -> Self {
let first_gate = gate.transform[0];
let second_gate = gate.transform[1];
let qubit_position = QuantumPosition::new(
first_gate.initial_position * self.initial_position()
+ first_gate.possible_position * self.possible_position(),
second_gate.initial_position * self.initial_position()
+ second_gate.possible_position * self.possible_position(),
);
Qubit::new(qubit_position)
}
/// [`Qubit::measure`] will measure a [`Qubit`] position in complex vector space,
/// determined by [`Qubit::initial_position`], and return a [`bool`] for it's
/// "truthy" state.
///
/// It is how [`Qubit`] superposition is observed.
///
/// # Example
/// [`Qubit::measure`] can be used to observe a [`Qubit`] after it passes through
/// a [`QuantumGate`]:
/// ```rust
/// use rquant::quantum::types::{qubit::Qubit, quantum_gate::QuantumGate};
///
/// fn observe_phased_qubit(qubit: Qubit) -> bool {
/// qubit.apply_gate(&QuantumGate::PHASE).measure()
/// }
/// ```
pub fn measure(&self) -> bool {
let prob_zero = self.initial_position().norm_sqr();
let mut rng = rand::rng();
rng.random_bool(prob_zero)
}
/// [`Qubit::initial_position`] will retrieve the current initial position
/// of the [`Qubit`] that calls it.
///
/// # Example
/// [`Qubit::initial_position`] can be used to get the initial position of
/// a [`Qubit`]:
/// ```rust
/// use num_complex::Complex;
/// use rquant::quantum::types::qubit::Qubit;
///
/// fn get_qubit_initial_position(qubit: Qubit) -> Complex<f64> {
/// qubit.initial_position()
/// }
/// ```
pub fn initial_position(&self) -> Complex<f64> {
self.position().initial_position
}
/// [`Qubit::possible_position`] will retrieve the current possible position
/// of the [`Qubit`] that calls it.
///
/// # Example
/// [`Qubit::possible_position`] can be used to get the possible position of
/// a [`Qubit`]:
/// ```rust
/// use num_complex::Complex;
/// use rquant::quantum::types::qubit::Qubit;
///
/// fn get_qubit_possible_position(qubit: Qubit) -> Complex<f64> {
/// qubit.possible_position()
/// }
/// ```
pub fn possible_position(&self) -> Complex<f64> {
self.position().possible_position
}
/// [`Qubit::position`] will retrieve the current position of the [`Qubit`]
/// that calls it.
fn position(&self) -> QuantumPosition {
self.positions
.first()
.cloned()
.expect("Must have an initial qubit position.")
}
}
/// Implement the `!` operator for [`Qubit`].
impl ops::Not for Qubit {
type Output = Self;
/// Flips amplitudes of a [`Qubit`] (analagous to a typical NOT gate), and can be expressed
/// by prefixing the `!` symbol to a [`Qubit`].
///
/// The gate can be represented by the following matrix:
/// $$\begin{pmatrix}0&1\\\1&0\end{pmatrix}$$
///
/// # Example
/// Can be used to invert a [`Qubit`]:
/// ```rust
/// use rquant::quantum::types::qubit::Qubit;
///
/// fn flip_qubit(qubit: Qubit) -> Qubit {
/// !qubit
/// }
/// ```
fn not(self) -> Self::Output {
self.apply_gate(&QuantumGate::new(QuantumOperator::NOT))
}
}
/// Implement the [`fmt::Display`] trait for [`Qubit`].
impl fmt::Display for Qubit {
/// Gets the [`String`] representation of a [`Qubit`], based on it's [`QuantumPosition`].
///
/// It will produce the ["Bra-Ket"](https://en.wikipedia.org/wiki/Bra-ket_notation) notation
/// of the [`Qubit`]. For instance, [`Qubit::zero`] will be presented as:
/// $$ 1|0\rangle $$
///
/// While a more complex [`Qubit`], such as [`Qubit::back_quarter_turn`] will be presented as:
/// $$ 0|0-1i\rangle $$
///
/// # Example
/// Can be used to print a [`Qubit`] to the console:
/// ```rust
/// use rquant::quantum::types::qubit::Qubit;
///
/// fn print_qubit(qubit: Qubit) {
/// println!("{qubit}");
/// }
/// ```
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let initial_includes_imaginary = self.initial_position().im != 0.0;
let alpha = if initial_includes_imaginary {
self.initial_position().to_string()
} else {
self.initial_position().re.to_string()
};
let possible_includes_imaginary = self.possible_position().im != 0.0;
let beta = if possible_includes_imaginary {
self.possible_position().to_string()
} else {
self.possible_position().re.to_string()
};
write!(f, "{}|{}〉", alpha, beta)
}
}