1use std::cell::Ref;
2use std::rc::Rc;
3
4use itertools::Itertools;
5use rustc_hash::FxHashMap;
6
7use crate::contractionpath::contraction_tree::node::{
8 child_node, parent_node, Node, NodeRef, WeakNodeRef,
9};
10use crate::contractionpath::paths::validate_path;
11use crate::contractionpath::{ContractionPath, SimplePath, SimplePathRef};
12use crate::tensornetwork::tensor::{CompositeTensor, LeafTensor};
13
14pub mod balancing;
15mod node;
16mod utils;
17
18#[derive(Default, Debug, Clone)]
20pub struct ContractionTree {
21 nodes: FxHashMap<usize, NodeRef>,
23 partitions: FxHashMap<usize, Vec<usize>>,
25 root: WeakNodeRef,
27}
28
29impl ContractionTree {
30 pub fn node(&self, node_id: usize) -> Ref<'_, Node> {
32 let borrow = &self.nodes[&node_id];
33 borrow.as_ref().borrow()
34 }
35
36 pub fn root_id(&self) -> Option<usize> {
38 self.root.upgrade().map(|node| node.borrow().id())
39 }
40
41 pub const fn partitions(&self) -> &FxHashMap<usize, Vec<usize>> {
42 &self.partitions
43 }
44
45 fn from_contraction_path_recurse(
48 tensor: &CompositeTensor,
49 path: &ContractionPath,
50 nodes: &mut FxHashMap<usize, NodeRef>,
51 partitions: &mut FxHashMap<usize, Vec<usize>>,
52 prefix: &[usize],
53 ) {
54 let mut scratch = FxHashMap::default();
55
56 for (path_id, path) in path.nested.iter().sorted_by_key(|&(path_id, _)| *path_id) {
58 let composite_tensor = tensor.tensor(*path_id).as_composite().unwrap();
59 let mut new_prefix = prefix.to_owned();
60 new_prefix.push(*path_id);
61 Self::from_contraction_path_recurse(
62 composite_tensor,
63 path,
64 nodes,
65 partitions,
66 &new_prefix,
67 );
68 scratch.insert(*path_id, Rc::clone(&nodes[&(nodes.len() - 1)]));
69 }
70
71 for (tensor_idx, tensor) in tensor.tensors().iter().enumerate() {
73 if tensor.is_leaf() {
74 let mut nested_tensor_idx = prefix.to_owned();
75 nested_tensor_idx.push(tensor_idx);
76 let new_node = child_node(nodes.len(), nested_tensor_idx);
77 scratch.insert(tensor_idx, Rc::clone(&new_node));
78 nodes.insert(nodes.len(), new_node);
79 }
80 }
81
82 for (i_path, j_path) in &path.toplevel {
84 let i = &scratch[i_path];
85 let j = &scratch[j_path];
86 let parent = parent_node(nodes.len(), i, j);
87
88 scratch.insert(*i_path, Rc::clone(&parent));
89 nodes.insert(nodes.len(), parent);
90 scratch.remove(j_path);
91 }
92 partitions
93 .entry(prefix.len())
94 .or_default()
95 .push(nodes.len() - 1);
96 }
97
98 #[must_use]
102 pub fn from_contraction_path(tensor: &CompositeTensor, path: &ContractionPath) -> Self {
103 validate_path(path);
104 let mut nodes = FxHashMap::default();
105 let mut partitions = FxHashMap::default();
106 Self::from_contraction_path_recurse(tensor, path, &mut nodes, &mut partitions, &[]);
107 let root = Rc::downgrade(&nodes[&(nodes.len() - 1)]);
108 Self {
109 nodes,
110 partitions,
111 root,
112 }
113 }
114
115 fn leaf_ids_recurse(node: &Node, leaf_indices: &mut Vec<usize>) {
116 if node.is_leaf() {
117 leaf_indices.push(node.id());
118 } else {
119 Self::leaf_ids_recurse(&node.left_child().unwrap().as_ref().borrow(), leaf_indices);
120 Self::leaf_ids_recurse(&node.right_child().unwrap().as_ref().borrow(), leaf_indices);
121 }
122 }
123
124 pub fn leaf_ids(&self, node_id: usize) -> Vec<usize> {
126 let mut leaf_indices = Vec::new();
127 let node = self.node(node_id);
128 Self::leaf_ids_recurse(&node, &mut leaf_indices);
129 leaf_indices
130 }
131
132 fn remove_subtree_recurse(&mut self, node_id: usize) {
134 if self.node(node_id).is_leaf() {
135 let node = &self.nodes[&node_id];
137 if let Some(parent_id) = node.borrow().parent_id() {
138 if self.nodes.contains_key(&parent_id) {
139 self.nodes[&parent_id]
140 .borrow_mut()
141 .remove_child(node.borrow().id());
142 }
143 }
144 node.borrow_mut().remove_parent();
145 return;
146 }
147
148 let node = self.nodes.remove(&node_id).unwrap();
149 let node = node.borrow();
150
151 if let Some(id) = node.left_child_id() {
152 self.remove_subtree_recurse(id);
153 }
154 if let Some(id) = node.right_child_id() {
155 self.remove_subtree_recurse(id);
156 }
157 }
158
159 pub(crate) fn remove_subtree(&mut self, node_id: usize) {
161 self.remove_subtree_recurse(node_id);
162 }
163
164 pub(crate) fn add_path_as_subtree(
167 &mut self,
168 path: &ContractionPath,
169 parent_id: usize,
170 leaf_tensor_indices: &[usize],
171 ) -> usize {
172 validate_path(path);
173 assert!(self.nodes.contains_key(&parent_id));
174
175 let mut index = 0;
176 let mut scratch = FxHashMap::default();
178
179 for &tensor_index in leaf_tensor_indices {
181 scratch.insert(tensor_index, Rc::clone(&self.nodes[&tensor_index]));
182 }
183
184 assert!(
186 path.is_simple(),
187 "Constructor not implemented for nested Tensors"
188 );
189 for (i_path, j_path) in &path.toplevel {
190 index = self.next_id(index);
192 let i = &scratch[i_path];
193 let j = &scratch[j_path];
194
195 assert!(
197 i.borrow().parent_id().is_none(),
198 "Tensor {i_path} is already used in another contraction"
199 );
200 assert!(
201 j.borrow().parent_id().is_none(),
202 "Tensor {j_path} is already used in another contraction"
203 );
204
205 let parent = parent_node(index, i, j);
206 scratch.insert(*i_path, Rc::clone(&parent));
207 scratch.remove(j_path);
208 self.nodes.insert(index, parent);
210 }
211
212 let new_parent = &self.nodes[&parent_id];
214 new_parent
215 .borrow_mut()
216 .add_child(Rc::downgrade(&self.nodes[&index]));
217
218 let new_child = &self.nodes[&index];
219 new_child.borrow_mut().set_parent(Rc::downgrade(new_parent));
220
221 index
222 }
223
224 fn remove_communication_path(&mut self, partition_ids: &[usize]) {
225 for partition_id in partition_ids {
226 let mut parent_id = self.node(*partition_id).parent_id();
227 while let Some(tensor_id) = parent_id {
228 parent_id = self.node(tensor_id).parent_id();
229 self.nodes.remove(&tensor_id);
230 }
231 }
232 }
233
234 fn replace_communication_path(
235 &mut self,
236 partition_ids: Vec<usize>,
237 communication_path: SimplePathRef,
238 ) {
239 self.remove_communication_path(&partition_ids);
241
242 let mut communication_ids = partition_ids;
244 let mut next_id = self.next_id(0);
245 for (i, j) in communication_path {
246 let left_child = communication_ids[*i];
247 let right_child = communication_ids[*j];
248 let new_parent =
249 parent_node(next_id, &self.nodes[&left_child], &self.nodes[&right_child]);
250 self.nodes.insert(next_id, new_parent);
251
252 communication_ids[*i] = next_id;
253 next_id = self.next_id(next_id);
254 }
255
256 self.root = Rc::downgrade(self.nodes.iter().max_by_key(|entry| entry.0).unwrap().1);
258 }
259
260 fn tree_weights_recurse(
261 node: &Node,
262 tn: &CompositeTensor,
263 weights: &mut FxHashMap<usize, f64>,
264 scratch: &mut FxHashMap<usize, LeafTensor>,
265 cost_function: fn(&LeafTensor, &LeafTensor) -> f64,
266 ) {
267 if node.is_leaf() {
268 let Some(tensor_index) = &node.tensor_index() else {
269 panic!("All leaf nodes should have a tensor index")
270 };
271 weights.insert(node.id(), 0f64);
272 scratch.insert(
273 node.id(),
274 tn.nested_tensor(tensor_index).as_leaf().unwrap().clone(),
275 );
276 return;
277 }
278
279 let left_child = &node.left_child().unwrap();
280 let right_child = &node.right_child().unwrap();
281 let left_ref = left_child.as_ref().borrow();
282 let right_ref = right_child.as_ref().borrow();
283
284 Self::tree_weights_recurse(&left_ref, tn, weights, scratch, cost_function);
286 Self::tree_weights_recurse(&right_ref, tn, weights, scratch, cost_function);
287
288 let t1 = &scratch[&left_ref.id()];
289 let t2 = &scratch[&right_ref.id()];
290
291 let cost = weights[&left_ref.id()] + weights[&right_ref.id()] + cost_function(t1, t2);
292
293 weights.insert(node.id(), cost);
294 scratch.insert(node.id(), t1 ^ t2);
295 }
296
297 pub fn tree_weights(
304 &self,
305 node_id: usize,
306 tn: &CompositeTensor,
307 cost_function: fn(&LeafTensor, &LeafTensor) -> f64,
308 ) -> FxHashMap<usize, f64> {
309 let mut weights = FxHashMap::default();
310 let mut scratch = FxHashMap::default();
311 let node = self.node(node_id);
312 Self::tree_weights_recurse(&node, tn, &mut weights, &mut scratch, cost_function);
313 weights
314 }
315
316 fn to_contraction_path_recurse(node: &Node, path: &mut SimplePath, replace: bool) -> usize {
323 if node.is_leaf() {
324 return node.id();
325 }
326
327 let (Some(left_child), Some(right_child)) = (node.left_child(), node.right_child()) else {
329 panic!("All parents should have two children")
330 };
331
332 let mut t1_id =
334 Self::to_contraction_path_recurse(&left_child.as_ref().borrow(), path, replace);
335 let mut t2_id =
336 Self::to_contraction_path_recurse(&right_child.as_ref().borrow(), path, replace);
337 if t2_id < t1_id {
338 (t1_id, t2_id) = (t2_id, t1_id);
339 }
340
341 path.push((t1_id, t2_id));
343
344 if replace {
346 t1_id
347 } else {
348 node.id()
349 }
350 }
351
352 pub fn to_flat_contraction_path(&self, node_id: usize, replace: bool) -> SimplePath {
357 let node = self.node(node_id);
358 let mut path = Vec::new();
359 Self::to_contraction_path_recurse(&node, &mut path, replace);
360 path
361 }
362
363 fn next_id(&self, mut init: usize) -> usize {
364 while self.nodes.contains_key(&init) {
365 init += 1;
366 }
367 init
368 }
369
370 pub fn tensor(&self, node_id: usize, tensor: &CompositeTensor) -> LeafTensor {
379 let leaf_nodes = self.leaf_ids(node_id);
380 let mut new_tensor = LeafTensor::default();
381
382 for leaf_id in leaf_nodes {
383 new_tensor = &new_tensor
384 ^ tensor
385 .nested_tensor(self.node(leaf_id).tensor_index().as_ref().unwrap())
386 .as_leaf()
387 .unwrap();
388 }
389 new_tensor
390 }
391}
392
393fn populate_subtree_tensor_map_recursive(
394 contraction_tree: &ContractionTree,
395 node_id: usize,
396 node_tensor_map: &mut FxHashMap<usize, LeafTensor>,
397 tensor_network: &CompositeTensor,
398 height_limit: Option<usize>,
399) -> (LeafTensor, usize) {
400 let node = contraction_tree.node(node_id);
401
402 if node.is_leaf() {
403 let tensor_index = node.tensor_index().unwrap();
404 let t = tensor_network
405 .nested_tensor(tensor_index)
406 .as_leaf()
407 .unwrap();
408 node_tensor_map.insert(node.id(), t.clone());
409 (t.clone(), 0)
410 } else {
411 let (t1, new_height1) = populate_subtree_tensor_map_recursive(
412 contraction_tree,
413 node.left_child_id().unwrap(),
414 node_tensor_map,
415 tensor_network,
416 height_limit,
417 );
418 let (t2, new_height2) = populate_subtree_tensor_map_recursive(
419 contraction_tree,
420 node.right_child_id().unwrap(),
421 node_tensor_map,
422 tensor_network,
423 height_limit,
424 );
425 let t12 = &t1 ^ &t2;
426 if let Some(height_limit) = height_limit {
427 if new_height1 < height_limit && new_height2 < height_limit {
428 node_tensor_map.insert(node.id(), t12.clone());
429 }
430 } else {
431 node_tensor_map.insert(node.id(), t12.clone());
432 }
433
434 (t12, new_height1.max(new_height2) + 1)
435 }
436}
437
438fn populate_subtree_tensor_map(
451 contraction_tree: &ContractionTree,
452 node_id: usize,
453 tensor_network: &CompositeTensor,
454 height_limit: Option<usize>,
455) -> FxHashMap<usize, LeafTensor> {
456 let mut node_tensor_map = FxHashMap::default();
457 let _ = populate_subtree_tensor_map_recursive(
458 contraction_tree,
459 node_id,
460 &mut node_tensor_map,
461 tensor_network,
462 height_limit,
463 );
464 node_tensor_map
465}
466
467fn populate_leaf_node_tensor_map(
477 contraction_tree: &ContractionTree,
478 node_id: usize,
479 tensor_network: &CompositeTensor,
480) -> FxHashMap<usize, LeafTensor> {
481 let mut node_tensor_map = FxHashMap::default();
482 for leaf_node_id in contraction_tree.leaf_ids(node_id) {
483 node_tensor_map.insert(
484 leaf_node_id,
485 contraction_tree.tensor(leaf_node_id, tensor_network),
486 );
487 }
488 node_tensor_map
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 use std::cell::RefCell;
496 use std::iter::zip;
497 use std::rc::Weak;
498
499 use itertools::Itertools;
500
501 use crate::contractionpath::contraction_cost::contract_cost_tensors;
502 use crate::contractionpath::contraction_tree::node::{child_node, parent_node};
503 use crate::contractionpath::contraction_tree::{ContractionTree, Node};
504 use crate::contractionpath::ssa_replace_ordering;
505 use crate::path;
506 use crate::tensornetwork::tensor::{EdgeIndex, Tensor};
507
508 fn setup_simple() -> (CompositeTensor, ContractionPath, FxHashMap<EdgeIndex, u64>) {
509 let bond_dims =
510 FxHashMap::from_iter([(0, 5), (1, 2), (2, 6), (3, 8), (4, 1), (5, 3), (6, 4)]);
511 (
512 CompositeTensor::new(vec![
513 LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims),
514 LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims),
515 LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims),
516 ]),
517 path![(0, 1), (2, 0)],
518 bond_dims,
519 )
520 }
521
522 fn setup_complex() -> (CompositeTensor, ContractionPath, FxHashMap<EdgeIndex, u64>) {
523 let bond_dims = FxHashMap::from_iter([
524 (0, 27),
525 (1, 18),
526 (2, 12),
527 (3, 15),
528 (4, 5),
529 (5, 3),
530 (6, 18),
531 (7, 22),
532 (8, 45),
533 (9, 65),
534 (10, 5),
535 ]);
536 (
537 CompositeTensor::new(vec![
538 LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims),
539 LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims),
540 LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims),
541 LeafTensor::new_from_map(vec![6, 8, 9], &bond_dims),
542 LeafTensor::new_from_map(vec![10, 8, 9], &bond_dims),
543 LeafTensor::new_from_map(vec![5, 1, 0], &bond_dims),
544 ]),
545 path![(1, 5), (0, 1), (3, 4), (2, 3), (0, 2)],
546 bond_dims,
547 )
548 }
549
550 fn setup_unbalanced() -> (CompositeTensor, ContractionPath) {
551 let bond_dims = FxHashMap::from_iter([
552 (0, 27),
553 (1, 18),
554 (2, 12),
555 (3, 15),
556 (4, 5),
557 (5, 3),
558 (6, 18),
559 (7, 22),
560 (8, 45),
561 (9, 65),
562 (10, 5),
563 (11, 17),
564 ]);
565 (
566 CompositeTensor::new(vec![
567 LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims),
568 LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims),
569 LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims),
570 LeafTensor::new_from_map(vec![6, 8, 9], &bond_dims),
571 LeafTensor::new_from_map(vec![10, 8, 9], &bond_dims),
572 LeafTensor::new_from_map(vec![5, 1, 0], &bond_dims),
573 ]),
574 path![(0, 1), (2, 0), (3, 2), (4, 3), (5, 4)],
575 )
576 }
577
578 fn setup_nested() -> (CompositeTensor, ContractionPath) {
579 let bond_dims = FxHashMap::from_iter([
580 (0, 27),
581 (1, 18),
582 (2, 12),
583 (3, 15),
584 (4, 5),
585 (5, 3),
586 (6, 18),
587 (7, 22),
588 (8, 45),
589 (9, 65),
590 (10, 5),
591 (11, 17),
592 ]);
593
594 let t0 = LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims);
595 let t1 = LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims);
596 let t2 = LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims);
597 let t3 = LeafTensor::new_from_map(vec![6, 8, 9], &bond_dims);
598 let t4 = LeafTensor::new_from_map(vec![5, 1, 0], &bond_dims);
599 let t5 = LeafTensor::new_from_map(vec![10, 8, 9], &bond_dims);
600
601 let t01 = CompositeTensor::new(vec![t0, t1]);
602 let t23 = CompositeTensor::new(vec![t2, t3]);
603 let t45 = CompositeTensor::new(vec![t4, t5]);
604 let tensor_network = CompositeTensor::new(vec![t01, t23, t45]);
605 (
606 tensor_network,
607 path![{(0, [(0, 1)]), (1, [(0, 1)]), (2, [(0, 1)])}, (0, 1), (0, 2)],
608 )
609 }
610
611 fn setup_double_nested() -> (CompositeTensor, ContractionPath) {
612 let bond_dims = FxHashMap::from_iter([
613 (0, 27),
614 (1, 18),
615 (2, 12),
616 (3, 15),
617 (4, 5),
618 (5, 3),
619 (6, 18),
620 (7, 22),
621 (8, 45),
622 (9, 65),
623 (10, 5),
624 (11, 17),
625 ]);
626
627 let t0 = LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims);
628 let t1 = LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims);
629 let t2 = LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims);
630 let t3 = LeafTensor::new_from_map(vec![6, 8, 9], &bond_dims);
631 let t4 = LeafTensor::new_from_map(vec![5, 1, 0], &bond_dims);
632 let t5 = LeafTensor::new_from_map(vec![10, 8, 9], &bond_dims);
633
634 let t01 = CompositeTensor::new(vec![t0, t1]);
635 let t01: Tensor = t01.into();
636 let t012 = CompositeTensor::new(vec![t01, t2.into()]);
637 let t34 = CompositeTensor::new(vec![t3, t4]);
638 let t34: Tensor = t34.into();
639 let t345 = CompositeTensor::new(vec![t34, t5.into()]);
640 let tensor_network = CompositeTensor::new(vec![t012, t345]);
641 (
642 tensor_network,
643 path![
644 {
645 (0, [{(0, [(0, 1)])}, (0, 1)]),
646 (1, [{(0, [(0, 1)])}, (0, 1)]),
647 },
648 (0, 1)
649 ],
650 )
651 }
652
653 impl PartialEq for Node {
654 fn eq(&self, other: &Self) -> bool {
655 self.id() == other.id()
656 && self.left_child_id() == other.left_child_id()
657 && self.right_child_id() == other.right_child_id()
658 && self.parent_id() == other.parent_id()
659 && self.tensor_index() == other.tensor_index()
660 }
661 }
662
663 #[test]
664 fn test_from_contraction_path_simple() {
665 let (tensor, path, _) = setup_simple();
666 let ContractionTree { nodes, root, .. } =
667 ContractionTree::from_contraction_path(&tensor, &path);
668
669 let node0 = child_node(0, vec![0]);
670 let node1 = child_node(1, vec![1]);
671 let node2 = child_node(2, vec![2]);
672
673 let node3 = parent_node(3, &node0, &node1);
674 let node4 = parent_node(4, &node2, &node3);
675
676 let ref_root = Rc::clone(&node4);
677 let ref_nodes = [node0, node1, node2, node3, node4];
678
679 for (key, ref_node) in ref_nodes.iter().enumerate() {
680 let node = &nodes[&key];
681 assert_eq!(node, ref_node);
682 }
683 assert_eq!(root.upgrade().unwrap(), ref_root);
684 }
685
686 #[test]
687 fn test_from_contraction_path_complex() {
688 let (tensor, path, _) = setup_complex();
689 let ContractionTree { nodes, root, .. } =
690 ContractionTree::from_contraction_path(&tensor, &path);
691
692 let node0 = child_node(0, vec![0]);
693 let node1 = child_node(1, vec![1]);
694 let node2 = child_node(2, vec![2]);
695 let node3 = child_node(3, vec![3]);
696 let node4 = child_node(4, vec![4]);
697 let node5 = child_node(5, vec![5]);
698
699 let node6 = parent_node(6, &node1, &node5);
700 let node7 = parent_node(7, &node0, &node6);
701 let node8 = parent_node(8, &node3, &node4);
702 let node9 = parent_node(9, &node2, &node8);
703 let node10 = parent_node(10, &node7, &node9);
704
705 let ref_root = Rc::clone(&node10);
706 let ref_nodes = [
707 node0, node1, node2, node3, node4, node5, node6, node7, node8, node9, node10,
708 ];
709
710 for (key, ref_node) in ref_nodes.iter().enumerate() {
711 let node = &nodes[&key];
712 assert_eq!(node, ref_node);
713 }
714 assert_eq!(root.upgrade().unwrap(), ref_root);
715 }
716
717 #[test]
718 fn test_from_contraction_path_nested() {
719 let (tensor, path) = setup_nested();
720 let ContractionTree { nodes, root, .. } =
721 ContractionTree::from_contraction_path(&tensor, &path);
722
723 let node0 = child_node(0, vec![0, 0]);
724 let node1 = child_node(1, vec![0, 1]);
725 let node3 = child_node(3, vec![1, 0]);
726 let node4 = child_node(4, vec![1, 1]);
727 let node6 = child_node(6, vec![2, 0]);
728 let node7 = child_node(7, vec![2, 1]);
729
730 let node2 = parent_node(2, &node0, &node1);
731 let node5 = parent_node(5, &node3, &node4);
732 let node8 = parent_node(8, &node6, &node7);
733 let node9 = parent_node(9, &node2, &node5);
734 let node10 = parent_node(10, &node9, &node8);
735
736 let ref_root = Rc::clone(&node10);
737 let ref_nodes = [
738 node0, node1, node2, node3, node4, node5, node6, node7, node8, node9, node10,
739 ];
740
741 for (key, ref_node) in ref_nodes.iter().enumerate() {
742 let node = &nodes[&key];
743 assert_eq!(node, ref_node);
744 }
745 assert_eq!(root.upgrade().unwrap(), ref_root);
746 }
747
748 #[test]
749 fn test_from_contraction_path_double_nested() {
750 let (tensor, path) = setup_double_nested();
751 let ContractionTree { nodes, root, .. } =
752 ContractionTree::from_contraction_path(&tensor, &path);
753
754 let node0 = child_node(0, vec![0, 0, 0]);
755 let node1 = child_node(1, vec![0, 0, 1]);
756 let node3 = child_node(3, vec![0, 1]);
757 let node5 = child_node(5, vec![1, 0, 0]);
758 let node6 = child_node(6, vec![1, 0, 1]);
759 let node8 = child_node(8, vec![1, 1]);
760
761 let node2 = parent_node(2, &node0, &node1);
762 let node4 = parent_node(4, &node2, &node3);
763 let node7 = parent_node(7, &node5, &node6);
764 let node9 = parent_node(9, &node7, &node8);
765 let node10 = parent_node(10, &node4, &node9);
766
767 let ref_root = Rc::clone(&node10);
768 let ref_nodes = [
769 node0, node1, node2, node3, node4, node5, node6, node7, node8, node9, node10,
770 ];
771
772 for (key, ref_node) in ref_nodes.iter().enumerate() {
773 let node = &nodes[&key];
774 assert_eq!(node, ref_node);
775 }
776 assert_eq!(root.upgrade().unwrap(), ref_root);
777 }
778
779 #[test]
780 fn test_leaf_ids_simple() {
781 let (tn, path, _) = setup_simple();
782 let tree = ContractionTree::from_contraction_path(&tn, &path);
783
784 assert_eq!(tree.leaf_ids(4), vec![2, 0, 1]);
785 assert_eq!(tree.leaf_ids(3), vec![0, 1]);
786 assert_eq!(tree.leaf_ids(2), vec![2]);
787 }
788
789 #[test]
790 fn test_leaf_ids_complex() {
791 let (tn, path, _) = setup_complex();
792 let tree = ContractionTree::from_contraction_path(&tn, &path);
793
794 assert_eq!(tree.leaf_ids(10), vec![0, 1, 5, 2, 3, 4]);
795 assert_eq!(tree.leaf_ids(9), vec![2, 3, 4]);
796 assert_eq!(tree.leaf_ids(8), vec![3, 4]);
797 assert_eq!(tree.leaf_ids(7), vec![0, 1, 5]);
798 assert_eq!(tree.leaf_ids(6), vec![1, 5]);
799 assert_eq!(tree.leaf_ids(3), vec![3]);
800 }
801
802 #[test]
803 fn test_leaf_ids_nested() {
804 let (tn, path) = setup_nested();
805 let tree = ContractionTree::from_contraction_path(&tn, &path);
806 assert_eq!(tree.leaf_ids(10), vec![0, 1, 3, 4, 6, 7]);
807 assert_eq!(tree.leaf_ids(9), vec![0, 1, 3, 4]);
808 assert_eq!(tree.leaf_ids(8), vec![6, 7]);
809 assert_eq!(tree.leaf_ids(5), vec![3, 4]);
810 assert_eq!(tree.leaf_ids(2), vec![0, 1]);
811 }
812
813 #[test]
814 fn test_leaf_ids_double_nested() {
815 let (tn, path) = setup_double_nested();
816 let tree = ContractionTree::from_contraction_path(&tn, &path);
817
818 assert_eq!(tree.leaf_ids(10), vec![0, 1, 3, 5, 6, 8]);
819 assert_eq!(tree.leaf_ids(9), vec![5, 6, 8]);
820 assert_eq!(tree.leaf_ids(7), vec![5, 6]);
821 assert_eq!(tree.leaf_ids(4), vec![0, 1, 3]);
822 assert_eq!(tree.leaf_ids(2), vec![0, 1]);
823 }
824
825 #[test]
826 fn test_remove_subtree() {
827 let (tn, path) = setup_nested();
828 let mut tree = ContractionTree::from_contraction_path(&tn, &path);
829
830 tree.remove_subtree(8);
831
832 let ContractionTree { nodes, root, .. } = tree;
833
834 let node0 = child_node(0, vec![0, 0]);
835 let node1 = child_node(1, vec![0, 1]);
836 let node3 = child_node(3, vec![1, 0]);
837 let node4 = child_node(4, vec![1, 1]);
838 let node6 = child_node(6, vec![2, 0]);
839 let node7 = child_node(7, vec![2, 1]);
840 let node2 = parent_node(2, &node0, &node1);
841 let node5 = parent_node(5, &node3, &node4);
842 let node9 = parent_node(9, &node2, &node5);
843 let node10 = Rc::new(RefCell::new(Node::new(
844 10,
845 Rc::downgrade(&node9),
846 Weak::new(),
847 Weak::new(),
848 None,
849 )));
850 node9.borrow_mut().set_parent(Rc::downgrade(&node10));
851
852 let ref_root = Rc::clone(&node10);
853 let ref_nodes = [
854 node0, node1, node2, node3, node4, node5, node6, node7, node9, node10,
855 ];
856 let mut range = (0..8).collect_vec();
857 range.extend(9..11);
858 for (key, ref_node) in zip(range.iter(), ref_nodes.iter()) {
859 let node = &nodes[key];
860 assert_eq!(node, ref_node);
861 }
862 assert_eq!(root.upgrade().unwrap(), ref_root);
863 }
864
865 #[test]
866 fn test_remove_trivial_subtree() {
867 let (tensor, path) = setup_nested();
868 let mut tree = ContractionTree::from_contraction_path(&tensor, &path);
869
870 tree.remove_subtree(7);
871
872 let ContractionTree { nodes, root, .. } = tree;
873
874 let node0 = child_node(0, vec![0, 0]);
875 let node1 = child_node(1, vec![0, 1]);
876 let node3 = child_node(3, vec![1, 0]);
877 let node4 = child_node(4, vec![1, 1]);
878 let node6 = child_node(6, vec![2, 0]);
879 let node7 = child_node(7, vec![2, 1]);
880 let node2 = parent_node(2, &node0, &node1);
881 let node5 = parent_node(5, &node3, &node4);
882 let node8 = Rc::new(RefCell::new(Node::new(
883 8,
884 Rc::downgrade(&node6),
885 Weak::new(),
886 Weak::new(),
887 None,
888 )));
889 let node9 = parent_node(9, &node2, &node5);
890 let node10 = parent_node(10, &node9, &node8);
891 node6.borrow_mut().set_parent(Rc::downgrade(&node8));
892
893 let ref_root = Rc::clone(&node10);
894 let ref_nodes = [
895 node0, node1, node2, node3, node4, node5, node6, node7, node8, node9, node10,
896 ];
897
898 for (key, ref_node) in ref_nodes.iter().enumerate() {
899 let node = &nodes[&key];
900 assert_eq!(node, ref_node);
901 }
902 assert_eq!(root.upgrade().unwrap(), ref_root);
903 }
904
905 #[test]
906 fn test_tree_weights_simple() {
907 let (tensor, path, _) = setup_simple();
908 let tree = ContractionTree::from_contraction_path(&tensor, &path);
909 let ref_weights = FxHashMap::from_iter([(1, 0.), (0, 0.), (2, 0.), (3, 3820.), (4, 4540.)]);
910 let weights = tree.tree_weights(4, &tensor, contract_cost_tensors);
911
912 assert_eq!(weights, ref_weights);
913 let ref_weights = FxHashMap::from_iter([(1, 0.), (0, 0.), (3, 3820.)]);
914 let weights = tree.tree_weights(3, &tensor, contract_cost_tensors);
915 assert_eq!(weights, ref_weights);
916
917 assert_eq!(weights, ref_weights);
918 let ref_weights = FxHashMap::from_iter([(2, 0.)]);
919 let weights = tree.tree_weights(2, &tensor, contract_cost_tensors);
920 assert_eq!(weights, ref_weights);
921 }
922
923 #[test]
924 fn test_tree_weights_complex() {
925 let (tensor, path, _) = setup_complex();
926 let tree = ContractionTree::from_contraction_path(&tensor, &path);
927 let ref_weights = FxHashMap::from_iter([
928 (0, 0.),
929 (1, 0.),
930 (2, 0.),
931 (3, 0.),
932 (4, 0.),
933 (5, 0.),
934 (6, 2098440.),
935 (7, 2120010.),
936 (8, 2105820.),
937 (9, 2116470.),
938 (10, 4237070.),
939 ]);
940 let weights = tree.tree_weights(10, &tensor, contract_cost_tensors);
941
942 assert_eq!(weights, ref_weights);
943 }
944
945 #[test]
946 fn test_to_contraction_path_simple() {
947 let (tensor, ref_path, _) = setup_simple();
948 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
949 let path = tree.to_flat_contraction_path(4, false);
950 let path = ssa_replace_ordering(&ContractionPath::simple(path));
951 assert_eq!(path, ref_path);
952 }
953
954 #[test]
955 fn test_to_contraction_path_complex() {
956 let (tensor, ref_path, _) = setup_complex();
957 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
958 let path = tree.to_flat_contraction_path(10, false);
959 let path = ssa_replace_ordering(&ContractionPath::simple(path));
960 assert_eq!(path, ref_path);
961 }
962
963 #[test]
964 fn test_to_contraction_path_unbalanced() {
965 let (tensor, ref_path) = setup_unbalanced();
966 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
967 let path = tree.to_flat_contraction_path(10, false);
968 let path = ssa_replace_ordering(&ContractionPath::simple(path));
969 assert_eq!(path, ref_path);
970 }
971
972 #[test]
973 fn test_populate_subtree_tensor_map_simple() {
974 let (tensor, ref_path, bond_dims) = setup_simple();
975 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
976 let mut node_tensor_map = FxHashMap::default();
977 populate_subtree_tensor_map_recursive(&tree, 4, &mut node_tensor_map, &tensor, None);
978
979 let ref_node_tensor_map = FxHashMap::from_iter([
980 (0, LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims)),
981 (1, LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims)),
982 (2, LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims)),
983 (3, LeafTensor::new_from_map(vec![4, 0, 1], &bond_dims)),
984 (4, LeafTensor::new_from_map(vec![5, 6, 0, 1], &bond_dims)),
985 ]);
986
987 for (key, value) in ref_node_tensor_map {
988 assert_eq!(node_tensor_map[&key].legs(), value.legs());
989 }
990 }
991
992 #[test]
993 fn test_populate_subtree_tensor_map_complex() {
994 let (tensor, ref_path, bond_dims) = setup_complex();
995 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
996 let mut node_tensor_map = FxHashMap::default();
997 populate_subtree_tensor_map_recursive(&tree, 10, &mut node_tensor_map, &tensor, None);
998
999 let ref_node_tensor_map = FxHashMap::from_iter([
1000 (0, LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims)),
1001 (1, LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims)),
1002 (2, LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims)),
1003 (3, LeafTensor::new_from_map(vec![6, 8, 9], &bond_dims)),
1004 (4, LeafTensor::new_from_map(vec![10, 8, 9], &bond_dims)),
1005 (5, LeafTensor::new_from_map(vec![5, 1, 0], &bond_dims)),
1006 (6, LeafTensor::new_from_map(vec![3, 2, 5], &bond_dims)),
1007 (7, LeafTensor::new_from_map(vec![4, 5], &bond_dims)),
1008 (8, LeafTensor::new_from_map(vec![6, 10], &bond_dims)),
1009 (9, LeafTensor::new_from_map(vec![4, 5, 10], &bond_dims)),
1010 (10, LeafTensor::new_from_map(vec![10], &bond_dims)),
1011 ]);
1012
1013 for (key, value) in ref_node_tensor_map {
1014 assert_eq!(node_tensor_map[&key].legs(), value.legs());
1015 }
1016 }
1017
1018 #[test]
1019 fn test_populate_subtree_tensor_map_height_limit() {
1020 let (tensor, ref_path, bond_dims) = setup_complex();
1021 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
1022 let node_tensor_map = populate_subtree_tensor_map(&tree, 10, &tensor, Some(1));
1023
1024 let ref_node_tensor_map = FxHashMap::from_iter([
1025 (0, LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims)),
1026 (1, LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims)),
1027 (2, LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims)),
1028 (3, LeafTensor::new_from_map(vec![6, 8, 9], &bond_dims)),
1029 (4, LeafTensor::new_from_map(vec![10, 8, 9], &bond_dims)),
1030 (5, LeafTensor::new_from_map(vec![5, 1, 0], &bond_dims)),
1031 (6, LeafTensor::new_from_map(vec![3, 2, 5], &bond_dims)),
1032 (8, LeafTensor::new_from_map(vec![6, 10], &bond_dims)),
1033 ]);
1034
1035 for (key, value) in ref_node_tensor_map {
1036 assert_eq!(node_tensor_map[&key].legs(), value.legs());
1037 }
1038 }
1039
1040 #[test]
1041 fn test_populate_leaf_node_tensor_map_simple() {
1042 let (tensor, ref_path, bond_dims) = setup_simple();
1043 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
1044
1045 let node_tensor_map = populate_leaf_node_tensor_map(&tree, 4, &tensor);
1046
1047 let ref_node_tensor_map = FxHashMap::from_iter([
1048 (0, LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims)),
1049 (1, LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims)),
1050 (2, LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims)),
1051 ]);
1052
1053 for (key, value) in ref_node_tensor_map {
1054 assert_eq!(node_tensor_map[&key].legs(), value.legs());
1055 }
1056 }
1057
1058 #[test]
1059 fn test_populate_leaf_node_tensor_map_complex() {
1060 let (tensor, ref_path, bond_dims) = setup_complex();
1061 let tree = ContractionTree::from_contraction_path(&tensor, &ref_path);
1062 let node_tensor_map = populate_subtree_tensor_map(&tree, 10, &tensor, None);
1063
1064 let ref_node_tensor_map = FxHashMap::from_iter([
1065 (0, LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims)),
1066 (1, LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims)),
1067 (2, LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims)),
1068 (3, LeafTensor::new_from_map(vec![6, 8, 9], &bond_dims)),
1069 (4, LeafTensor::new_from_map(vec![10, 8, 9], &bond_dims)),
1070 ]);
1071
1072 for (key, value) in ref_node_tensor_map {
1073 assert_eq!(node_tensor_map[&key].legs(), value.legs());
1074 }
1075 }
1076
1077 #[test]
1078 fn test_add_path_as_subtree() {
1079 let (tensor, path, _) = setup_complex();
1080
1081 let mut complex_tree = ContractionTree::from_contraction_path(&tensor, &path);
1082 complex_tree.remove_subtree(9);
1083 let new_path = path![(4, 2), (4, 3)];
1084
1085 complex_tree.add_path_as_subtree(&new_path, 10, &[3, 4, 2]);
1086
1087 let ContractionTree { nodes, root, .. } = complex_tree;
1088
1089 let node0 = child_node(0, vec![0]);
1090 let node1 = child_node(1, vec![1]);
1091 let node2 = child_node(2, vec![2]);
1092 let node3 = child_node(3, vec![3]);
1093 let node4 = child_node(4, vec![4]);
1094 let node5 = child_node(5, vec![5]);
1095 let node6 = parent_node(6, &node1, &node5);
1096 let node7 = parent_node(7, &node0, &node6);
1097 let node8 = parent_node(8, &node4, &node2);
1098 let node9 = parent_node(9, &node8, &node3);
1099 let node10 = parent_node(10, &node7, &node9);
1100
1101 let ref_root = Rc::clone(&node10);
1102 let ref_nodes = [
1103 node0, node1, node2, node3, node4, node5, node6, node7, node8, node9, node10,
1104 ];
1105
1106 for (key, ref_node) in ref_nodes.iter().enumerate() {
1107 let node = &nodes[&key];
1108 assert_eq!(node, ref_node);
1109 }
1110 assert_eq!(root.upgrade().unwrap(), ref_root);
1111 }
1112
1113 #[test]
1114 #[should_panic = "Tensor 2 is already used in another contraction"]
1115 fn test_add_path_as_subtree_invalid_path() {
1116 let (tensor, path, _) = setup_complex();
1117
1118 let mut complex_tree = ContractionTree::from_contraction_path(&tensor, &path);
1119 complex_tree.remove_subtree(8);
1120 let new_path = path![(4, 2), (4, 3)];
1121
1122 complex_tree.add_path_as_subtree(&new_path, 9, &[3, 4, 2]);
1123 }
1124
1125 #[test]
1126 fn test_remove_communication_path() {
1127 let (tensor, path) = setup_nested();
1128 let mut complex_tree = ContractionTree::from_contraction_path(&tensor, &path);
1129 let partition_ids = vec![2, 5, 8];
1130 complex_tree.remove_communication_path(&partition_ids);
1131 assert!(!complex_tree.nodes.contains_key(&9));
1132 assert!(!complex_tree.nodes.contains_key(&10));
1133 assert!(complex_tree.root_id().is_none());
1134 }
1135
1136 #[test]
1137 fn test_replace_communication_path() {
1138 let (tensor, path) = setup_nested();
1139 let mut complex_tree = ContractionTree::from_contraction_path(&tensor, &path);
1140 let partition_ids = vec![2, 5, 8];
1141 complex_tree.replace_communication_path(partition_ids, &[(0, 2), (1, 0)]);
1142
1143 let ContractionTree { nodes, root, .. } = complex_tree;
1144
1145 let node2 = child_node(2, vec![]);
1146 let node5 = child_node(5, vec![]);
1147 let node8 = child_node(8, vec![]);
1148 let node9 = parent_node(9, &node2, &node8);
1149 let node10 = parent_node(10, &node5, &node9);
1150
1151 assert_eq!(nodes[&9], node9);
1152 assert_eq!(nodes[&10], node10);
1153 assert_eq!(root.upgrade().unwrap(), node10);
1154 }
1155}