quantrs2_core/quantum_walk/
multi.rs1use super::discrete::DiscreteQuantumWalk;
4use super::graph::{CoinOperator, Graph};
5use crate::error::{QuantRS2Error, QuantRS2Result};
6use scirs2_core::ndarray::{Array1, Array2};
7use scirs2_core::Complex64;
8
9pub struct MultiWalkerQuantumWalk {
11 graph: Graph,
12 num_walkers: usize,
13 state: Array1<Complex64>,
15 single_walker_dim: usize,
17}
18
19impl MultiWalkerQuantumWalk {
20 pub fn new(graph: Graph, num_walkers: usize) -> Self {
22 let single_walker_dim = graph.num_vertices;
23 let total_dim = single_walker_dim.pow(num_walkers as u32);
24
25 Self {
26 graph,
27 num_walkers,
28 state: Array1::zeros(total_dim),
29 single_walker_dim,
30 }
31 }
32
33 pub fn initialize_positions(&mut self, positions: &[usize]) -> QuantRS2Result<()> {
35 if positions.len() != self.num_walkers {
36 return Err(QuantRS2Error::InvalidInput(
37 "Number of positions must match number of walkers".to_string(),
38 ));
39 }
40
41 for &pos in positions {
42 if pos >= self.single_walker_dim {
43 return Err(QuantRS2Error::InvalidInput(format!(
44 "Position {pos} out of bounds"
45 )));
46 }
47 }
48
49 self.state.fill(Complex64::new(0.0, 0.0));
51
52 let index = self.positions_to_index(positions);
54 self.state[index] = Complex64::new(1.0, 0.0);
55
56 Ok(())
57 }
58
59 pub fn initialize_entangled_bell_state(
61 &mut self,
62 pos1: usize,
63 pos2: usize,
64 ) -> QuantRS2Result<()> {
65 if self.num_walkers != 2 {
66 return Err(QuantRS2Error::InvalidInput(
67 "Bell state initialization only works for 2 walkers".to_string(),
68 ));
69 }
70
71 self.state.fill(Complex64::new(0.0, 0.0));
72
73 let amplitude = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
74
75 let idx1 = self.positions_to_index(&[pos1, pos2]);
77 let idx2 = self.positions_to_index(&[pos2, pos1]);
78
79 self.state[idx1] = amplitude;
80 self.state[idx2] = amplitude;
81
82 Ok(())
83 }
84
85 fn positions_to_index(&self, positions: &[usize]) -> usize {
87 let mut index = 0;
88 let mut multiplier = 1;
89
90 for &pos in positions.iter().rev() {
91 index += pos * multiplier;
92 multiplier *= self.single_walker_dim;
93 }
94
95 index
96 }
97
98 fn index_to_positions(&self, mut index: usize) -> Vec<usize> {
100 let mut positions = Vec::with_capacity(self.num_walkers);
101
102 for _ in 0..self.num_walkers {
103 positions.push(index % self.single_walker_dim);
104 index /= self.single_walker_dim;
105 }
106
107 positions.reverse();
108 positions
109 }
110
111 pub fn step_independent(&mut self) {
113 let mut new_state = Array1::zeros(self.state.len());
114
115 for (index, &litude) in self.state.iter().enumerate() {
116 if amplitude.norm_sqr() < 1e-15 {
117 continue;
118 }
119
120 let positions = self.index_to_positions(index);
121
122 let mut total_neighbors = 1;
124 for &pos in &positions {
125 total_neighbors *= self.graph.degree(pos).max(1);
126 }
127
128 let neighbor_amplitude = amplitude / (total_neighbors as f64).sqrt();
129
130 self.add_neighbor_amplitudes(
132 &positions,
133 0,
134 &mut Vec::new(),
135 neighbor_amplitude,
136 &mut new_state,
137 );
138 }
139
140 self.state = new_state;
141 }
142
143 fn add_neighbor_amplitudes(
145 &self,
146 original_positions: &[usize],
147 walker_idx: usize,
148 current_positions: &mut Vec<usize>,
149 amplitude: Complex64,
150 new_state: &mut Array1<Complex64>,
151 ) {
152 if walker_idx >= self.num_walkers {
153 let index = self.positions_to_index(current_positions);
154 new_state[index] += amplitude;
155 return;
156 }
157
158 let pos = original_positions[walker_idx];
159 let neighbors = &self.graph.edges[pos];
160
161 if neighbors.is_empty() {
162 current_positions.push(pos);
164 self.add_neighbor_amplitudes(
165 original_positions,
166 walker_idx + 1,
167 current_positions,
168 amplitude,
169 new_state,
170 );
171 current_positions.pop();
172 } else {
173 for &neighbor in neighbors {
174 current_positions.push(neighbor);
175 self.add_neighbor_amplitudes(
176 original_positions,
177 walker_idx + 1,
178 current_positions,
179 amplitude,
180 new_state,
181 );
182 current_positions.pop();
183 }
184 }
185 }
186
187 pub fn marginal_probabilities(&self, walker_idx: usize) -> Vec<f64> {
189 let mut probs = vec![0.0; self.single_walker_dim];
190
191 for (index, &litude) in self.state.iter().enumerate() {
192 let positions = self.index_to_positions(index);
193 probs[positions[walker_idx]] += amplitude.norm_sqr();
194 }
195
196 probs
197 }
198
199 pub fn entanglement_entropy(&self) -> f64 {
201 if self.num_walkers != 2 {
202 return 0.0; }
204
205 let mut reduced_dm = Array2::zeros((self.single_walker_dim, self.single_walker_dim));
207
208 for i in 0..self.single_walker_dim {
209 for j in 0..self.single_walker_dim {
210 for k in 0..self.single_walker_dim {
211 let idx1 = self.positions_to_index(&[i, k]);
212 let idx2 = self.positions_to_index(&[j, k]);
213
214 reduced_dm[[i, j]] += self.state[idx1].conj() * self.state[idx2];
215 }
216 }
217 }
218
219 let trace = reduced_dm.diag().mapv(|x: Complex64| x.re).sum();
221 -trace * trace.ln() }
223}
224
225pub struct DecoherentQuantumWalk {
227 base_walk: DiscreteQuantumWalk,
228 decoherence_rate: f64,
229 measurement_probability: f64,
230}
231
232impl DecoherentQuantumWalk {
233 pub fn new(graph: Graph, coin_operator: CoinOperator, decoherence_rate: f64) -> Self {
235 Self {
236 base_walk: DiscreteQuantumWalk::new(graph, coin_operator),
237 decoherence_rate,
238 measurement_probability: 0.0,
239 }
240 }
241
242 pub fn initialize_position(&mut self, position: usize) {
244 self.base_walk.initialize_position(position);
245 }
246
247 pub fn step(&mut self) {
249 self.base_walk.step();
251
252 self.apply_decoherence();
254 }
255
256 fn apply_decoherence(&mut self) {
258 if self.decoherence_rate <= 0.0 {
259 return;
260 }
261
262 let quantum_probs = self.base_walk.position_probabilities();
264
265 let mut classical_probs = vec![0.0; quantum_probs.len()];
267 for (v, &prob) in quantum_probs.iter().enumerate() {
268 if prob > 0.0 {
269 let degree = self.base_walk.graph.degree(v) as f64;
270 if degree > 0.0 {
271 for &neighbor in &self.base_walk.graph.edges[v] {
272 classical_probs[neighbor] += prob / degree;
273 }
274 } else {
275 classical_probs[v] += prob; }
277 }
278 }
279
280 let quantum_weight = 1.0 - self.decoherence_rate;
282 let classical_weight = self.decoherence_rate;
283
284 for v in 0..quantum_probs.len() {
286 let mixed_prob =
287 quantum_weight * quantum_probs[v] + classical_weight * classical_probs[v];
288
289 if quantum_probs[v] > 0.0 {
291 let scale_factor = (mixed_prob / quantum_probs[v]).sqrt();
292
293 for coin in 0..self.base_walk.coin_dimension {
294 let idx = self.base_walk.state_index(v, coin);
295 if idx < self.base_walk.state.len() {
296 self.base_walk.state[idx] *= scale_factor;
297 }
298 }
299 }
300 }
301
302 let total_norm: f64 = self.base_walk.state.iter().map(|c| c.norm_sqr()).sum();
304 if total_norm > 0.0 {
305 let norm_factor = 1.0 / total_norm.sqrt();
306 for amplitude in &mut self.base_walk.state {
307 *amplitude *= norm_factor;
308 }
309 }
310 }
311
312 pub fn position_probabilities(&self) -> Vec<f64> {
314 self.base_walk.position_probabilities()
315 }
316
317 pub const fn set_decoherence_rate(&mut self, rate: f64) {
319 self.decoherence_rate = rate.clamp(0.0, 1.0);
320 }
321}