1use crate::algorithms::four_d::{GraphNode4D, GraphProperties, TemporalEdge};
24use crate::algorithms::mps::{get_tensor, mps_apply_gate_2site, mps_norm_sq, set_tensor};
25
26#[derive(Clone, Copy, Debug)]
28pub struct GridDims {
29 pub nx: usize,
30 pub ny: usize,
31 pub nz: usize,
32}
33
34#[derive(Clone, Copy, Debug)]
36pub struct SiteCoord {
37 pub sx: usize,
38 pub sy: usize,
39 pub sz: usize,
40}
41
42pub fn site_id(sx: usize, sy: usize, sz: usize, tl: usize, nx: usize, ny: usize, nz: usize) -> u64 {
46 (tl * nz * ny * nx + sz * ny * nx + sy * nx + sx) as u64
47}
48
49pub fn build_tnet4d(nx: usize, ny: usize, nz: usize, depth: usize) -> Vec<GraphNode4D> {
57 let total = nx * ny * nz * depth;
58 let mut nodes: Vec<GraphNode4D> = Vec::with_capacity(total);
59
60 for tl in 0..depth {
62 for sz in 0..nz {
63 for sy in 0..ny {
64 for sx in 0..nx {
65 let id = site_id(sx, sy, sz, tl, nx, ny, nz);
66 let mut node = GraphNode4D {
67 id,
68 x: sx as f32,
69 y: sy as f32,
70 z: sz as f32,
71 begin_ts: tl as u64,
72 end_ts: tl as u64 + 1,
73 properties: GraphProperties::new(),
74 successors: Vec::new(),
75 };
76 set_tensor(&mut node, [1, 2, 1], vec![1.0, 0.0]);
78 nodes.push(node);
79 }
80 }
81 }
82 }
83
84 let directions: &[(i32, i32, i32)] = &[
86 (1, 0, 0),
87 (-1, 0, 0),
88 (0, 1, 0),
89 (0, -1, 0),
90 (0, 0, 1),
91 (0, 0, -1),
92 ];
93 for tl in 0..depth {
94 for sz in 0..nz {
95 for sy in 0..ny {
96 for sx in 0..nx {
97 let src_id = site_id(sx, sy, sz, tl, nx, ny, nz);
98 for &(dx, dy, dz) in directions {
99 let nx2 = sx as i32 + dx;
100 let ny2 = sy as i32 + dy;
101 let nz2 = sz as i32 + dz;
102 if nx2 < 0 || nx2 >= nx as i32 {
103 continue;
104 }
105 if ny2 < 0 || ny2 >= ny as i32 {
106 continue;
107 }
108 if nz2 < 0 || nz2 >= nz as i32 {
109 continue;
110 }
111 let dst_id =
112 site_id(nx2 as usize, ny2 as usize, nz2 as usize, tl, nx, ny, nz);
113 let src_idx = src_id as usize;
114 nodes[src_idx].successors.push(TemporalEdge {
115 dst: dst_id,
116 weight: 1.0, begin_ts: tl as u64,
118 end_ts: tl as u64, });
120 }
121 }
122 }
123 }
124 }
125
126 for tl in 0..depth.saturating_sub(1) {
128 for sz in 0..nz {
129 for sy in 0..ny {
130 for sx in 0..nx {
131 let src_id = site_id(sx, sy, sz, tl, nx, ny, nz);
132 let dst_id = site_id(sx, sy, sz, tl + 1, nx, ny, nz);
133 let src_idx = src_id as usize;
134 nodes[src_idx].successors.push(TemporalEdge {
135 dst: dst_id,
136 weight: 1.0, begin_ts: tl as u64,
138 end_ts: tl as u64 + 1, });
140 }
141 }
142 }
143 }
144
145 nodes
146}
147
148pub fn tnet4d_find(
152 nodes: &[GraphNode4D],
153 sx: usize,
154 sy: usize,
155 sz: usize,
156 tl: usize,
157 dims: GridDims,
158) -> Option<usize> {
159 if sx >= dims.nx || sy >= dims.ny || sz >= dims.nz {
160 return None;
161 }
162 let target_id = site_id(sx, sy, sz, tl, dims.nx, dims.ny, dims.nz);
163 nodes.iter().position(|n| n.id == target_id)
164}
165
166pub fn tnet4d_apply_gate_1site(
173 nodes: &mut [GraphNode4D],
174 sx: usize,
175 sy: usize,
176 sz: usize,
177 tl: usize,
178 dims: GridDims,
179 gate: &[f32],
180) {
181 if let Some(idx) = tnet4d_find(nodes, sx, sy, sz, tl, dims) {
182 let (shape, data) = match get_tensor(&nodes[idx]) {
183 Some(t) => t,
184 None => return,
185 };
186 let (chi_l, d, chi_r) = (shape[0], shape[1], shape[2]);
187 let mut new_data = vec![0.0f32; chi_l * d * chi_r];
188 for a in 0..chi_l {
189 for s_out in 0..d {
190 for b in 0..chi_r {
191 let mut val = 0.0f32;
192 for s_in in 0..d {
193 val += gate[s_out * d + s_in] * data[a * d * chi_r + s_in * chi_r + b];
194 }
195 new_data[a * d * chi_r + s_out * chi_r + b] = val;
196 }
197 }
198 }
199 set_tensor(&mut nodes[idx], shape, new_data);
200 }
201}
202
203pub fn tnet4d_apply_gate_2site(
211 nodes: &mut [GraphNode4D],
212 site0: SiteCoord,
213 site1: SiteCoord,
214 tl: usize,
215 dims: GridDims,
216 gate: &[f32],
217 chi_max: usize,
218) -> usize {
219 let idx_a = match tnet4d_find(nodes, site0.sx, site0.sy, site0.sz, tl, dims) {
220 Some(i) => i,
221 None => return 0,
222 };
223 let idx_b = match tnet4d_find(nodes, site1.sx, site1.sy, site1.sz, tl, dims) {
224 Some(i) => i,
225 None => return 0,
226 };
227 mps_apply_gate_2site(nodes, idx_a, idx_b, gate, chi_max)
228}
229
230pub fn tnet4d_norm_sq(nodes: &[GraphNode4D], nx: usize, ny: usize, nz: usize, depth: usize) -> f64 {
240 let mut total_norm = 1.0f64;
243 for sz in 0..nz {
244 for sy in 0..ny {
245 for sx in 0..nx {
246 let dims = GridDims { nx, ny, nz };
247 let column: Vec<GraphNode4D> = (0..depth)
248 .filter_map(|tl| {
249 tnet4d_find(nodes, sx, sy, sz, tl, dims).map(|idx| nodes[idx].clone())
250 })
251 .collect();
252 total_norm *= mps_norm_sq(&column);
253 }
254 }
255 }
256 total_norm
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 fn hadamard() -> Vec<f32> {
265 let s = std::f32::consts::FRAC_1_SQRT_2;
266 vec![s, s, s, -s]
267 }
268
269 #[test]
272 fn test_build_node_count_single_layer() {
273 let nodes = build_tnet4d(2, 2, 1, 1);
274 assert_eq!(nodes.len(), 4, "2×2×1 grid × 1 layer = 4 nodes");
275 }
276
277 #[test]
280 fn test_build_node_count_two_layers() {
281 let nodes = build_tnet4d(2, 2, 1, 2);
282 assert_eq!(nodes.len(), 8, "2×2×1 grid × 2 layers = 8 nodes");
283 }
284
285 #[test]
288 fn test_temporal_edges_connect_layers() {
289 let nodes = build_tnet4d(2, 2, 1, 2);
290 let src_id = site_id(0, 0, 0, 0, 2, 2, 1);
292 let dst_id = site_id(0, 0, 0, 1, 2, 2, 1);
293 let src = nodes.iter().find(|n| n.id == src_id).expect("src node");
294 let has_temporal = src.successors.iter().any(|e| e.dst == dst_id);
295 assert!(has_temporal, "no temporal edge from layer 0 to layer 1");
296 let dst = nodes.iter().find(|n| n.id == dst_id).expect("dst node");
298 assert!(
299 dst.begin_ts > src.begin_ts,
300 "temporal dst should be at a later time layer"
301 );
302 }
303
304 #[test]
307 fn test_spatial_edges_connect_neighbours() {
308 let nodes = build_tnet4d(2, 2, 1, 1);
309 let src_id = site_id(0, 0, 0, 0, 2, 2, 1);
311 let right_id = site_id(1, 0, 0, 0, 2, 2, 1);
312 let up_id = site_id(0, 1, 0, 0, 2, 2, 1);
313 let src = nodes.iter().find(|n| n.id == src_id).expect("src");
314 let has_right = src.successors.iter().any(|e| e.dst == right_id);
315 let has_up = src.successors.iter().any(|e| e.dst == up_id);
316 assert!(has_right, "missing spatial edge to (1,0,0)");
317 assert!(has_up, "missing spatial edge to (0,1,0)");
318 let right = nodes.iter().find(|n| n.id == right_id).expect("right");
320 assert_eq!(
321 right.begin_ts, src.begin_ts,
322 "spatial neighbour must be same layer"
323 );
324 }
325
326 #[test]
329 fn test_find_locates_correct_node() {
330 let nodes = build_tnet4d(2, 2, 1, 2);
331 let dims = GridDims {
332 nx: 2,
333 ny: 2,
334 nz: 1,
335 };
336 let idx = tnet4d_find(&nodes, 1, 1, 0, 0, dims).expect("node must exist");
338 let expected_id = site_id(1, 1, 0, 0, 2, 2, 1);
339 assert_eq!(nodes[idx].id, expected_id);
340 assert!(tnet4d_find(&nodes, 5, 0, 0, 0, dims).is_none());
342 }
343
344 #[test]
347 fn test_product_state_norm_is_one() {
348 let nodes = build_tnet4d(2, 2, 1, 1);
349 let norm = tnet4d_norm_sq(&nodes, 2, 2, 1, 1);
350 assert!((norm - 1.0).abs() < 1e-5, "norm = {norm}");
351 }
352
353 fn cnot_gate() -> Vec<f32> {
354 vec![
355 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
356 ]
357 }
358
359 #[test]
362 fn test_hadamard_all_sites_preserves_norm() {
363 let mut nodes = build_tnet4d(2, 2, 1, 1);
364 let h = hadamard();
365 let dims = GridDims {
366 nx: 2,
367 ny: 2,
368 nz: 1,
369 };
370 for sx in 0..2 {
371 for sy in 0..2 {
372 tnet4d_apply_gate_1site(&mut nodes, sx, sy, 0, 0, dims, &h);
373 }
374 }
375 let norm = tnet4d_norm_sq(&nodes, 2, 2, 1, 1);
376 assert!((norm - 1.0).abs() < 1e-5, "norm after H = {norm}");
377 }
378
379 #[test]
382 fn test_tnet4d_2site_gate_creates_entanglement() {
383 let mut nodes = build_tnet4d(2, 2, 1, 1);
384 let dims = GridDims {
385 nx: 2,
386 ny: 2,
387 nz: 1,
388 };
389 tnet4d_apply_gate_1site(&mut nodes, 0, 0, 0, 0, dims, &hadamard());
391 let site0 = SiteCoord {
393 sx: 0,
394 sy: 0,
395 sz: 0,
396 };
397 let site1 = SiteCoord {
398 sx: 1,
399 sy: 0,
400 sz: 0,
401 };
402 let chi_new = tnet4d_apply_gate_2site(&mut nodes, site0, site1, 0, dims, &cnot_gate(), 2);
403 assert_eq!(
404 chi_new, 2,
405 "Bell pair between spatial neighbours: bond must be 2"
406 );
407 }
408}