quantrs2_sim/holographic_quantum_error_correction/
encoding.rs1use scirs2_core::ndarray::Array2;
7use scirs2_core::Complex64;
8use std::f64::consts::PI;
9
10use crate::error::Result;
11
12use super::config::HolographicCodeType;
13use super::simulator::HolographicQECSimulator;
14
15impl HolographicQECSimulator {
16 pub fn create_holographic_encoding_matrix(
18 &self,
19 boundary_dim: usize,
20 bulk_dim: usize,
21 ) -> Result<Array2<Complex64>> {
22 let mut encoding_matrix = Array2::zeros((bulk_dim, boundary_dim));
23
24 match self.config.error_correction_code {
25 HolographicCodeType::AdSRindler => {
26 self.create_ads_rindler_encoding(&mut encoding_matrix)?;
27 }
28 HolographicCodeType::HolographicStabilizer => {
29 self.create_holographic_stabilizer_encoding(&mut encoding_matrix)?;
30 }
31 HolographicCodeType::BulkGeometry => {
32 self.create_bulk_geometry_encoding(&mut encoding_matrix)?;
33 }
34 HolographicCodeType::TensorNetwork => {
35 self.create_tensor_network_encoding(&mut encoding_matrix)?;
36 }
37 HolographicCodeType::HolographicSurface => {
38 self.create_holographic_surface_encoding(&mut encoding_matrix)?;
39 }
40 HolographicCodeType::PerfectTensor => {
41 self.create_perfect_tensor_encoding(&mut encoding_matrix)?;
42 }
43 HolographicCodeType::EntanglementEntropy => {
44 self.create_entanglement_entropy_encoding(&mut encoding_matrix)?;
45 }
46 HolographicCodeType::AdSCFTCode => {
47 self.create_ads_cft_encoding(&mut encoding_matrix)?;
48 }
49 }
50
51 Ok(encoding_matrix)
52 }
53
54 pub fn create_ads_rindler_encoding(
56 &self,
57 encoding_matrix: &mut Array2<Complex64>,
58 ) -> Result<()> {
59 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
60
61 for i in 0..bulk_dim {
63 for j in 0..boundary_dim {
64 let rindler_factor = self.calculate_rindler_factor(i, j);
65 let entanglement_factor = self.calculate_entanglement_factor(i, j);
66
67 encoding_matrix[[i, j]] = Complex64::new(rindler_factor * entanglement_factor, 0.0);
68 }
69 }
70
71 Self::normalize_encoding_matrix(encoding_matrix)?;
73 Ok(())
74 }
75
76 #[must_use]
78 pub fn calculate_rindler_factor(&self, bulk_index: usize, boundary_index: usize) -> f64 {
79 let rindler_horizon = self.config.ads_radius;
80 let bulk_position = (bulk_index as f64) / f64::from(1 << self.config.bulk_qubits);
81 let boundary_position =
82 (boundary_index as f64) / f64::from(1 << self.config.boundary_qubits);
83
84 let factor = (rindler_horizon * bulk_position).cosh()
86 * (2.0 * PI).mul_add(boundary_position, PI / 4.0).cos();
87
88 factor.abs().max(1e-10)
89 }
90
91 #[must_use]
93 pub fn calculate_entanglement_factor(&self, bulk_index: usize, boundary_index: usize) -> f64 {
94 let mutual_information = self.calculate_mutual_information(bulk_index, boundary_index);
95 let entanglement_entropy = self.calculate_entanglement_entropy(bulk_index, boundary_index);
96
97 (mutual_information * entanglement_entropy).sqrt()
98 }
99
100 pub(crate) fn calculate_mutual_information(
102 &self,
103 bulk_index: usize,
104 boundary_index: usize,
105 ) -> f64 {
106 let bulk_entropy = self.calculate_region_entropy(bulk_index, true);
107 let boundary_entropy = self.calculate_region_entropy(boundary_index, false);
108 let joint_entropy = self.calculate_joint_entropy(bulk_index, boundary_index);
109
110 bulk_entropy + boundary_entropy - joint_entropy
111 }
112
113 pub(crate) fn calculate_entanglement_entropy(
115 &self,
116 bulk_index: usize,
117 boundary_index: usize,
118 ) -> f64 {
119 let area = self.calculate_rt_surface_area(bulk_index, boundary_index);
121 let gravitational_constant = 1.0; area / (4.0 * gravitational_constant)
124 }
125
126 pub(crate) fn calculate_rt_surface_area(
128 &self,
129 bulk_index: usize,
130 boundary_index: usize,
131 ) -> f64 {
132 let bulk_position = (bulk_index as f64) / f64::from(1 << self.config.bulk_qubits);
133 let boundary_position =
134 (boundary_index as f64) / f64::from(1 << self.config.boundary_qubits);
135
136 let radial_distance = (bulk_position - boundary_position).abs();
138 let ads_factor = self.config.ads_radius / radial_distance.mul_add(radial_distance, 1.0);
139
140 ads_factor * self.config.central_charge
141 }
142
143 pub(crate) fn calculate_region_entropy(&self, region_index: usize, is_bulk: bool) -> f64 {
145 let max_index = if is_bulk {
146 Self::safe_dimension(self.config.bulk_qubits).unwrap_or(8)
147 } else {
148 Self::safe_dimension(self.config.boundary_qubits).unwrap_or(4)
149 };
150
151 let max_index = max_index.max(2);
153 let region_size = ((region_index % max_index) as f64 + 0.1) / (max_index as f64 + 0.2);
154
155 if region_size > 0.01 && region_size < 0.99 {
157 (-region_size).mul_add(
158 region_size.ln(),
159 -((1.0 - region_size) * (1.0 - region_size).ln()),
160 )
161 } else {
162 0.1
164 }
165 }
166
167 fn calculate_joint_entropy(&self, bulk_index: usize, boundary_index: usize) -> f64 {
169 let combined_entropy = self.calculate_region_entropy(bulk_index, true)
170 + self.calculate_region_entropy(boundary_index, false);
171
172 let correlation_factor = self.calculate_correlation_factor(bulk_index, boundary_index);
174 combined_entropy * (1.0 - correlation_factor)
175 }
176
177 pub(crate) fn calculate_correlation_factor(
179 &self,
180 bulk_index: usize,
181 boundary_index: usize,
182 ) -> f64 {
183 let bulk_position = (bulk_index as f64) / f64::from(1 << self.config.bulk_qubits);
184 let boundary_position =
185 (boundary_index as f64) / f64::from(1 << self.config.boundary_qubits);
186
187 let distance = (bulk_position - boundary_position).abs();
189 (-distance / self.config.ads_radius).exp()
190 }
191
192 pub(crate) fn create_holographic_stabilizer_encoding(
194 &self,
195 encoding_matrix: &mut Array2<Complex64>,
196 ) -> Result<()> {
197 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
198
199 for i in 0..bulk_dim {
201 for j in 0..boundary_dim {
202 let stabilizer_factor = Self::calculate_stabilizer_factor(i, j);
203 let holographic_factor = self.calculate_holographic_factor(i, j);
204
205 encoding_matrix[[i, j]] =
206 Complex64::new(stabilizer_factor * holographic_factor, 0.0);
207 }
208 }
209
210 Self::normalize_encoding_matrix(encoding_matrix)?;
211 Ok(())
212 }
213
214 fn calculate_stabilizer_factor(bulk_index: usize, boundary_index: usize) -> f64 {
216 let bulk_parity = f64::from(bulk_index.count_ones() % 2);
217 let boundary_parity = f64::from(boundary_index.count_ones() % 2);
218
219 if bulk_parity == boundary_parity {
221 1.0 / (1.0 + bulk_index as f64).sqrt()
222 } else {
223 -1.0 / (1.0 + bulk_index as f64).sqrt()
224 }
225 }
226
227 pub(crate) fn calculate_holographic_factor(
229 &self,
230 bulk_index: usize,
231 boundary_index: usize,
232 ) -> f64 {
233 let bulk_weight = f64::from(bulk_index.count_ones());
234 let boundary_weight = f64::from(boundary_index.count_ones());
235
236 let weight_correlation = (bulk_weight - boundary_weight).abs();
238 (-weight_correlation / self.config.central_charge).exp()
239 }
240
241 pub(crate) fn create_bulk_geometry_encoding(
243 &self,
244 encoding_matrix: &mut Array2<Complex64>,
245 ) -> Result<()> {
246 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
247
248 for i in 0..bulk_dim {
250 for j in 0..boundary_dim {
251 let geodesic_length = self.calculate_geodesic_length(i, j);
252 let geometric_factor = self.calculate_geometric_factor(i, j);
253
254 encoding_matrix[[i, j]] = Complex64::new(
255 (-geodesic_length / self.config.ads_radius).exp() * geometric_factor,
256 0.0,
257 );
258 }
259 }
260
261 Self::normalize_encoding_matrix(encoding_matrix)?;
262 Ok(())
263 }
264
265 pub(crate) fn calculate_geodesic_length(
267 &self,
268 bulk_index: usize,
269 boundary_index: usize,
270 ) -> f64 {
271 let bulk_position = (bulk_index as f64) / f64::from(1 << self.config.bulk_qubits);
272 let boundary_position =
273 (boundary_index as f64) / f64::from(1 << self.config.boundary_qubits);
274
275 let radial_bulk = 1.0 / (1.0 - bulk_position);
277 let radial_boundary = 1.0 / (1.0 - boundary_position);
278
279 self.config.ads_radius * (radial_bulk / radial_boundary).ln().abs()
280 }
281
282 pub(crate) fn calculate_geometric_factor(
284 &self,
285 bulk_index: usize,
286 boundary_index: usize,
287 ) -> f64 {
288 let bulk_curvature = self.calculate_bulk_curvature(bulk_index);
289 let boundary_curvature = self.calculate_boundary_curvature(boundary_index);
290
291 (bulk_curvature.abs() / boundary_curvature).sqrt()
292 }
293
294 fn calculate_bulk_curvature(&self, bulk_index: usize) -> f64 {
296 let position = (bulk_index as f64) / f64::from(1 << self.config.bulk_qubits);
297 let ads_curvature = -1.0 / (self.config.ads_radius * self.config.ads_radius);
298
299 ads_curvature * (1.0 - position).powi(2)
300 }
301
302 fn calculate_boundary_curvature(&self, boundary_index: usize) -> f64 {
304 let position = (boundary_index as f64) / f64::from(1 << self.config.boundary_qubits);
305
306 0.1f64
309 .mul_add((2.0 * PI * position).sin(), 1.0)
310 .abs()
311 .max(0.1)
312 }
313
314 pub(crate) fn create_tensor_network_encoding(
316 &self,
317 encoding_matrix: &mut Array2<Complex64>,
318 ) -> Result<()> {
319 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
320
321 for i in 0..bulk_dim {
323 for j in 0..boundary_dim {
324 let tensor_element = self.calculate_tensor_network_element(i, j);
325 encoding_matrix[[i, j]] = tensor_element;
326 }
327 }
328
329 Self::normalize_encoding_matrix(encoding_matrix)?;
330 Ok(())
331 }
332
333 pub(crate) fn calculate_tensor_network_element(
335 &self,
336 bulk_index: usize,
337 boundary_index: usize,
338 ) -> Complex64 {
339 let bulk_legs = Self::get_tensor_legs(bulk_index, true);
340 let boundary_legs = Self::get_tensor_legs(boundary_index, false);
341
342 let contraction_value = Self::contract_tensor_legs(&bulk_legs, &boundary_legs);
344
345 Complex64::new(contraction_value, 0.0)
346 }
347
348 fn get_tensor_legs(index: usize, is_bulk: bool) -> Vec<f64> {
350 let mut legs = Vec::new();
351 let num_legs = if is_bulk { 4 } else { 2 }; for i in 0..num_legs {
354 let leg_value = (((index >> i) & 1) as f64).mul_add(2.0, -1.0); legs.push(leg_value);
356 }
357
358 legs
359 }
360
361 fn contract_tensor_legs(bulk_legs: &[f64], boundary_legs: &[f64]) -> f64 {
363 let mut contraction = 1.0;
364
365 let min_legs = bulk_legs.len().min(boundary_legs.len());
367 for i in 0..min_legs {
368 contraction *= bulk_legs[i] * boundary_legs[i];
369 }
370
371 for leg in &bulk_legs[min_legs..] {
373 contraction *= leg;
374 }
375
376 contraction / (bulk_legs.len() as f64).sqrt()
377 }
378
379 pub(crate) fn create_holographic_surface_encoding(
381 &self,
382 encoding_matrix: &mut Array2<Complex64>,
383 ) -> Result<()> {
384 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
385
386 for i in 0..bulk_dim {
388 for j in 0..boundary_dim {
389 let surface_element = self.calculate_surface_code_element(i, j);
390 encoding_matrix[[i, j]] = surface_element;
391 }
392 }
393
394 Self::normalize_encoding_matrix(encoding_matrix)?;
395 Ok(())
396 }
397
398 fn calculate_surface_code_element(
400 &self,
401 bulk_index: usize,
402 boundary_index: usize,
403 ) -> Complex64 {
404 let bulk_x = bulk_index % (1 << (self.config.bulk_qubits / 2));
405 let bulk_y = bulk_index / (1 << (self.config.bulk_qubits / 2));
406 let boundary_x = boundary_index % (1 << (self.config.boundary_qubits / 2));
407 let boundary_y = boundary_index / (1 << (self.config.boundary_qubits / 2));
408
409 let x_parity = (bulk_x ^ boundary_x).count_ones() % 2;
411 let y_parity = (bulk_y ^ boundary_y).count_ones() % 2;
412
413 let amplitude = if x_parity == y_parity {
414 1.0 / (1.0 + (bulk_x + bulk_y) as f64).sqrt()
415 } else {
416 1e-8 / (2.0 + (bulk_x + bulk_y) as f64).sqrt()
418 };
419
420 Complex64::new(amplitude, 0.0)
421 }
422
423 pub(crate) fn create_perfect_tensor_encoding(
425 &self,
426 encoding_matrix: &mut Array2<Complex64>,
427 ) -> Result<()> {
428 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
429
430 for i in 0..bulk_dim {
432 for j in 0..boundary_dim {
433 let perfect_element = self.calculate_perfect_tensor_element(i, j);
434 encoding_matrix[[i, j]] = perfect_element;
435 }
436 }
437
438 Self::normalize_encoding_matrix(encoding_matrix)?;
439 Ok(())
440 }
441
442 fn calculate_perfect_tensor_element(
444 &self,
445 bulk_index: usize,
446 boundary_index: usize,
447 ) -> Complex64 {
448 let bulk_state = Self::index_to_state_vector(bulk_index, self.config.bulk_qubits);
449 let boundary_state =
450 Self::index_to_state_vector(boundary_index, self.config.boundary_qubits);
451
452 let overlap = Self::calculate_state_overlap(&bulk_state, &boundary_state);
454 let perfect_factor = Self::calculate_perfect_tensor_factor(bulk_index, boundary_index);
455
456 Complex64::new(overlap * perfect_factor, 0.0)
457 }
458
459 fn index_to_state_vector(index: usize, num_qubits: usize) -> Vec<f64> {
461 let mut state = vec![0.0; num_qubits];
462 for (i, elem) in state.iter_mut().enumerate() {
463 *elem = if (index >> i) & 1 == 1 { 1.0 } else { 0.0 };
464 }
465 state
466 }
467
468 fn calculate_state_overlap(state1: &[f64], state2: &[f64]) -> f64 {
470 let min_len = state1.len().min(state2.len());
471 let mut overlap = 0.0;
472
473 for i in 0..min_len {
474 overlap += state1[i] * state2[i];
475 }
476
477 overlap / (min_len as f64).sqrt()
478 }
479
480 fn calculate_perfect_tensor_factor(bulk_index: usize, boundary_index: usize) -> f64 {
482 let bulk_weight = f64::from(bulk_index.count_ones());
483 let boundary_weight = f64::from(boundary_index.count_ones());
484
485 if (bulk_weight - boundary_weight).abs() <= 1.0 {
487 1.0 / (1.0 + bulk_weight).sqrt()
488 } else {
489 1e-6 / (1.0 + (bulk_weight - boundary_weight).abs()).sqrt()
491 }
492 }
493
494 pub(crate) fn create_entanglement_entropy_encoding(
496 &self,
497 encoding_matrix: &mut Array2<Complex64>,
498 ) -> Result<()> {
499 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
500
501 for i in 0..bulk_dim {
503 for j in 0..boundary_dim {
504 let entropy_element = self.calculate_entanglement_entropy_element(i, j);
505 encoding_matrix[[i, j]] = entropy_element;
506 }
507 }
508
509 Self::normalize_encoding_matrix(encoding_matrix)?;
510 Ok(())
511 }
512
513 fn calculate_entanglement_entropy_element(
515 &self,
516 bulk_index: usize,
517 boundary_index: usize,
518 ) -> Complex64 {
519 let bulk_entropy = self.calculate_region_entropy(bulk_index, true);
520 let boundary_entropy = self.calculate_region_entropy(boundary_index, false);
521 let mutual_information = self.calculate_mutual_information(bulk_index, boundary_index);
522
523 let amplitude = (mutual_information / (bulk_entropy + boundary_entropy + 1e-10)).sqrt();
525
526 Complex64::new(amplitude, 0.0)
527 }
528
529 pub(crate) fn create_ads_cft_encoding(
531 &self,
532 encoding_matrix: &mut Array2<Complex64>,
533 ) -> Result<()> {
534 let (bulk_dim, boundary_dim) = encoding_matrix.dim();
535
536 for i in 0..bulk_dim {
538 for j in 0..boundary_dim {
539 let ads_cft_element = self.calculate_ads_cft_element(i, j);
540 encoding_matrix[[i, j]] = ads_cft_element;
541 }
542 }
543
544 Self::normalize_encoding_matrix(encoding_matrix)?;
545 Ok(())
546 }
547
548 pub(crate) fn calculate_ads_cft_element(
550 &self,
551 bulk_index: usize,
552 boundary_index: usize,
553 ) -> Complex64 {
554 let bulk_field = self.calculate_bulk_field_value(bulk_index);
555 let boundary_field = self.calculate_boundary_field_value(boundary_index);
556 let correlation_function = self.calculate_correlation_function(bulk_index, boundary_index);
557
558 let amplitude = bulk_field * boundary_field * correlation_function;
560
561 Complex64::new(amplitude, 0.0)
562 }
563
564 pub(crate) fn calculate_bulk_field_value(&self, bulk_index: usize) -> f64 {
566 let position = (bulk_index as f64) / f64::from(1 << self.config.bulk_qubits);
567 let radial_coordinate = 1.0 / (1.0 - position);
568
569 (radial_coordinate / self.config.ads_radius).powf(self.calculate_conformal_dimension())
571 }
572
573 pub(crate) fn calculate_boundary_field_value(&self, boundary_index: usize) -> f64 {
575 let position = (boundary_index as f64) / f64::from(1 << self.config.boundary_qubits);
576
577 (2.0 * PI * position).sin() / (1.0 + position).sqrt()
579 }
580
581 pub(crate) fn calculate_conformal_dimension(&self) -> f64 {
583 (self.config.central_charge / 12.0).sqrt()
585 }
586
587 pub(crate) fn calculate_correlation_function(
589 &self,
590 bulk_index: usize,
591 boundary_index: usize,
592 ) -> f64 {
593 let bulk_position = (bulk_index as f64) / f64::from(1 << self.config.bulk_qubits);
594 let boundary_position =
595 (boundary_index as f64) / f64::from(1 << self.config.boundary_qubits);
596
597 let distance = (bulk_position - boundary_position).abs();
599 let conformal_dimension = self.calculate_conformal_dimension();
600
601 1.0 / (1.0 + distance).powf(2.0 * conformal_dimension)
602 }
603
604 pub(crate) fn normalize_encoding_matrix(encoding_matrix: &mut Array2<Complex64>) -> Result<()> {
606 let (rows, cols) = encoding_matrix.dim();
607
608 for j in 0..cols {
610 let mut column_norm = 0.0;
611 for i in 0..rows {
612 column_norm += encoding_matrix[[i, j]].norm_sqr();
613 }
614
615 if column_norm > 1e-10 {
616 let norm_factor = 1.0 / column_norm.sqrt();
617 for i in 0..rows {
618 encoding_matrix[[i, j]] *= norm_factor;
619 }
620 } else {
621 if j < rows {
623 encoding_matrix[[j, j]] = Complex64::new(1e-6, 0.0);
624 } else {
625 encoding_matrix[[0, j]] = Complex64::new(1e-6, 0.0);
626 }
627 }
628 }
629
630 Ok(())
631 }
632}