1use num_complex::Complex64;
5
6use crate::{
7 contractionpath::{ContractionPath, SimplePathRef},
8 tensornetwork::tensor::LeafTensor,
9 tensornetwork::tensor::Tensor,
10};
11
12pub fn contract_cost_tensors(t_1: &LeafTensor, t_2: &LeafTensor) -> f64 {
27 let final_dims = t_1 ^ t_2;
28 let shared_dims = t_1 & t_2;
29
30 let single_loop_cost = shared_dims.size();
31 (single_loop_cost - 1f64).mul_add(2f64, single_loop_cost * 6f64) * final_dims.size()
32}
33
34#[inline]
49pub fn contract_op_cost_tensors(t_1: &LeafTensor, t_2: &LeafTensor) -> f64 {
50 let all_dims = t_1 | t_2;
51 all_dims.size()
52}
53
54#[inline]
69pub fn contract_size_tensors(t_1: &LeafTensor, t_2: &LeafTensor) -> f64 {
70 let diff = t_1 ^ t_2;
71 diff.size() + t_1.size() + t_2.size()
72}
73
74#[inline]
89pub fn contract_size_tensors_bytes(i: &LeafTensor, j: &LeafTensor) -> f64 {
90 contract_size_tensors(i, j) * std::mem::size_of::<Complex64>() as f64
91}
92
93#[inline]
101pub fn contract_path_cost(
102 inputs: &[Tensor],
103 contract_path: &ContractionPath,
104 only_count_ops: bool,
105) -> (f64, f64) {
106 let cost_function = if only_count_ops {
107 contract_op_cost_tensors
108 } else {
109 contract_cost_tensors
110 };
111 contract_path_custom_cost(inputs, contract_path, cost_function, contract_size_tensors)
112}
113
114fn contract_path_custom_cost(
122 inputs: &[Tensor],
123 contract_path: &ContractionPath,
124 cost_function: fn(&LeafTensor, &LeafTensor) -> f64,
125 size_function: fn(&LeafTensor, &LeafTensor) -> f64,
126) -> (f64, f64) {
127 let mut op_cost = 0f64;
128 let mut mem_cost = 0f64;
129 let mut inputs = inputs.to_vec();
130
131 for (i, path) in &contract_path.nested {
132 let composite = inputs[*i].as_composite().unwrap();
133 let costs =
134 contract_path_custom_cost(composite.tensors(), path, cost_function, size_function);
135 op_cost += costs.0;
136 mem_cost = mem_cost.max(costs.1);
137 inputs[*i] = composite.external_tensor().into();
138 }
139
140 for (i, j) in &contract_path.toplevel {
141 let ti = inputs[*i].as_leaf().unwrap();
142 let tj = inputs[*j].as_leaf().unwrap();
143 op_cost += cost_function(ti, tj);
144 let tij = ti ^ tj;
145 let new_mem_cost = size_function(ti, tj);
146 mem_cost = mem_cost.max(new_mem_cost);
147 inputs[*i] = tij.into();
148 }
149
150 (op_cost, mem_cost)
151}
152
153#[inline]
156pub fn communication_path_op_costs(
157 inputs: &[LeafTensor],
158 contract_path: SimplePathRef,
159 only_count_ops: bool,
160 tensor_cost: Option<&[f64]>,
161) -> ((f64, f64), f64) {
162 let (parallel_cost, _) =
163 communication_path_cost(inputs, contract_path, only_count_ops, true, tensor_cost);
164 let (serial_cost, mem_cost) =
165 communication_path_cost(inputs, contract_path, only_count_ops, false, tensor_cost);
166 ((parallel_cost, serial_cost), mem_cost)
167}
168
169pub fn communication_path_cost(
179 inputs: &[LeafTensor],
180 contract_path: SimplePathRef,
181 only_count_ops: bool,
182 only_critical_path: bool,
183 tensor_cost: Option<&[f64]>,
184) -> (f64, f64) {
185 let cost_function = if only_count_ops {
186 contract_op_cost_tensors
187 } else {
188 contract_cost_tensors
189 };
190 let tensor_cost = if let Some(tensor_cost) = tensor_cost {
191 assert_eq!(inputs.len(), tensor_cost.len());
192 tensor_cost
193 } else {
194 &vec![0f64; inputs.len()]
195 };
196 if inputs.len() == 1 {
197 return (tensor_cost[0], tensor_cost[0]);
198 }
199
200 communication_path_custom_cost(
201 inputs,
202 contract_path,
203 cost_function,
204 only_critical_path,
205 tensor_cost,
206 )
207}
208
209fn communication_path_custom_cost(
218 inputs: &[LeafTensor],
219 contract_path: SimplePathRef,
220 cost_function: fn(&LeafTensor, &LeafTensor) -> f64,
221 only_critical_path: bool,
222 tensor_cost: &[f64],
223) -> (f64, f64) {
224 let mut op_cost = 0f64;
225 let mut mem_cost = 0f64;
226 let mut inputs = inputs.to_vec();
227 let mut tensor_cost = tensor_cost.to_vec();
228
229 for &(i, j) in contract_path {
230 let ij = &inputs[i] ^ &inputs[j];
231 let new_mem_cost = contract_size_tensors(&inputs[i], &inputs[j]);
232 mem_cost = mem_cost.max(new_mem_cost);
233
234 op_cost = if only_critical_path {
235 cost_function(&inputs[i], &inputs[j]) + tensor_cost[i].max(tensor_cost[j])
236 } else {
237 cost_function(&inputs[i], &inputs[j]) + tensor_cost[i] + tensor_cost[j]
238 };
239 tensor_cost[i] = op_cost;
240 inputs[i] = ij;
241 }
242
243 (op_cost, mem_cost)
244}
245
246#[inline]
254pub fn compute_memory_requirements(
255 inputs: &[Tensor],
256 contract_path: &ContractionPath,
257 memory_estimator: fn(&LeafTensor, &LeafTensor) -> f64,
258) -> f64 {
259 fn id(_: &LeafTensor, _: &LeafTensor) -> f64 {
260 0.0
261 }
262 let (_, mem) = contract_path_custom_cost(inputs, contract_path, id, memory_estimator);
263 mem
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 use rustc_hash::FxHashMap;
271
272 use crate::path;
273 use crate::tensornetwork::tensor::CompositeTensor;
274
275 fn setup_simple() -> CompositeTensor {
276 let bond_dims =
277 FxHashMap::from_iter([(0, 5), (1, 2), (2, 6), (3, 8), (4, 1), (5, 3), (6, 4)]);
278 CompositeTensor::new(vec![
279 LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims),
280 LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims),
281 LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims),
282 ])
283 }
284
285 fn setup_complex() -> CompositeTensor {
286 let bond_dims = FxHashMap::from_iter([
287 (0, 5),
288 (1, 2),
289 (2, 6),
290 (3, 8),
291 (4, 1),
292 (5, 3),
293 (6, 4),
294 (7, 3),
295 (8, 2),
296 (9, 2),
297 ]);
298 let t1_tensors = vec![
299 LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims),
300 LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims),
301 LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims),
302 ];
303 let t1 = CompositeTensor::new(t1_tensors);
304
305 let t2_tensors = vec![
306 LeafTensor::new_from_map(vec![5, 6, 8], &bond_dims),
307 LeafTensor::new_from_map(vec![7, 8, 9], &bond_dims),
308 ];
309 let t2 = CompositeTensor::new(t2_tensors);
310 CompositeTensor::new(vec![t1, t2])
311 }
312
313 fn setup_parallel() -> Vec<LeafTensor> {
314 let bond_dims =
315 FxHashMap::from_iter([(0, 5), (1, 2), (2, 6), (3, 8), (4, 1), (5, 3), (6, 4)]);
316 vec![
317 LeafTensor::new_from_map(vec![4, 3, 2], &bond_dims),
318 LeafTensor::new_from_map(vec![0, 1, 3, 2], &bond_dims),
319 LeafTensor::new_from_map(vec![4, 5, 6], &bond_dims),
320 LeafTensor::new_from_map(vec![5, 6], &bond_dims),
321 ]
322 }
323
324 #[test]
325 fn test_contract_path_cost() {
326 let tn = setup_simple();
327 let (op_cost, mem_cost) = contract_path_cost(tn.tensors(), &path![(0, 1), (0, 2)], false);
328 assert_eq!(op_cost, 4540.);
329 assert_eq!(mem_cost, 538.);
330 let (op_cost, mem_cost) = contract_path_cost(tn.tensors(), &path![(0, 2), (0, 1)], false);
331 assert_eq!(op_cost, 49296.);
332 assert_eq!(mem_cost, 1176.);
333 }
334
335 #[test]
336 fn test_contract_complex_path_cost() {
337 let tn = setup_complex();
338 let (op_cost, mem_cost) = contract_path_cost(
339 tn.tensors(),
340 &path![{(0, [(0, 1), (0, 2)]), (1, [(0, 1)])}, (0, 1)],
341 false,
342 );
343 assert_eq!(op_cost, 11188.);
344 assert_eq!(mem_cost, 538.);
345 }
346
347 #[test]
348 fn test_contract_path_cost_only_ops() {
349 let tn = setup_simple();
350 let (op_cost, mem_cost) = contract_path_cost(tn.tensors(), &path![(0, 1), (0, 2)], true);
351 assert_eq!(op_cost, 600.);
352 assert_eq!(mem_cost, 538.);
353 let (op_cost, mem_cost) = contract_path_cost(tn.tensors(), &path![(0, 2), (0, 1)], true);
354 assert_eq!(op_cost, 6336.);
355 assert_eq!(mem_cost, 1176.);
356 }
357
358 #[test]
359 fn test_contract_path_complex_cost_only_ops() {
360 let tn = setup_complex();
361 let (op_cost, mem_cost) = contract_path_cost(
362 tn.tensors(),
363 &path![{(0, [(0, 1), (0, 2)]), (1, [(0, 1)])}, (0, 1)],
364 true,
365 );
366 assert_eq!(op_cost, 1464.);
367 assert_eq!(mem_cost, 538.);
368 }
369
370 #[test]
371 fn test_communication_path_cost_only_ops() {
372 let tensors = setup_parallel();
373 let (op_cost, mem_cost) =
374 communication_path_cost(&tensors, &[(0, 1), (2, 3), (0, 2)], true, true, None);
375 assert_eq!(op_cost, 490.);
376 assert_eq!(mem_cost, 538.);
377 }
378
379 #[test]
380 fn test_communication_path_cost() {
381 let tensors = setup_parallel();
382 let (op_cost, mem_cost) =
383 communication_path_cost(&tensors, &[(0, 1), (2, 3), (0, 1)], false, true, None);
384 assert_eq!(op_cost, 7564.);
385 assert_eq!(mem_cost, 538.);
386 }
387
388 #[test]
389 fn test_communication_path_cost_only_ops_with_partition_cost() {
390 let tensors = setup_parallel();
391 let tensor_cost = vec![20., 30., 80., 10.];
392 let (op_cost, mem_cost) = communication_path_cost(
393 &tensors,
394 &[(0, 1), (2, 3), (0, 2)],
395 true,
396 true,
397 Some(&tensor_cost),
398 );
399 assert_eq!(op_cost, 520.);
400 assert_eq!(mem_cost, 538.);
401 }
402
403 #[test]
404 fn test_communication_path_cost_with_partition_cost() {
405 let tensors = setup_parallel();
406 let tensor_cost = vec![20., 30., 80., 10.];
407 let (op_cost, mem_cost) = communication_path_cost(
408 &tensors,
409 &[(0, 1), (2, 3), (0, 1)],
410 false,
411 true,
412 Some(&tensor_cost),
413 );
414 assert_eq!(op_cost, 7594.);
415 assert_eq!(mem_cost, 538.);
416 }
417}