microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
source/resource_estimator/src/counts.rs
633lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | #[cfg(test)] |
| 5 | mod tests; |
| 6 | |
| 7 | use num_bigint::BigUint; |
| 8 | use num_complex::Complex; |
| 9 | use qsc::{Backend, interpret::Value}; |
| 10 | use rand::{Rng, SeedableRng, rngs::StdRng}; |
| 11 | use rustc_hash::FxHashMap; |
| 12 | use std::{array, cell::RefCell, f64::consts::PI, fmt::Debug, iter::Sum}; |
| 13 | |
| 14 | use crate::system::LogicalResourceCounts; |
| 15 | |
| 16 | /// Resource counter implementation |
| 17 | /// |
| 18 | /// This counter tracks all resources while executing a QIR program. It takes |
| 19 | /// care of qubit management, gate counting, and depth calculation. |
| 20 | pub struct LogicalCounter { |
| 21 | /// Stack of free qubits |
| 22 | free_list: Vec<usize>, |
| 23 | /// Next free qubit id, in case `free_list` is empty |
| 24 | next_free: usize, |
| 25 | /// Depth counter |
| 26 | max_layer: Vec<usize>, |
| 27 | /// Layers |
| 28 | layers: Vec<LayerInfo>, |
| 29 | /// T-count (excluded in rotation count) |
| 30 | t_count: usize, |
| 31 | /// Number of Z rotation gates (excluding Cliffords and T gates) |
| 32 | r_count: usize, |
| 33 | /// CCZ count (does not contribute to T count) |
| 34 | ccz_count: usize, |
| 35 | /// Number of single-qubit and multiple-qubit measurements |
| 36 | m_count: usize, |
| 37 | /// Global allocation barrier (when calling global barrier this is advanced |
| 38 | /// to allocate new qubits after the barrier) |
| 39 | allocation_barrier: usize, |
| 40 | /// Caching stack |
| 41 | caching_stack: Vec<String>, |
| 42 | /// Caching |
| 43 | caching_layers: FxHashMap<String, LayerCache>, |
| 44 | /// Repeating |
| 45 | repeats: Vec<RepeatEntry>, |
| 46 | /// Random number generator |
| 47 | rnd: RefCell<StdRng>, |
| 48 | } |
| 49 | |
| 50 | impl Default for LogicalCounter { |
| 51 | fn default() -> Self { |
| 52 | Self { |
| 53 | free_list: vec![], |
| 54 | next_free: 0, |
| 55 | max_layer: vec![], |
| 56 | layers: vec![], |
| 57 | t_count: 0, |
| 58 | r_count: 0, |
| 59 | ccz_count: 0, |
| 60 | m_count: 0, |
| 61 | allocation_barrier: 0, |
| 62 | caching_stack: vec![], |
| 63 | caching_layers: FxHashMap::default(), |
| 64 | repeats: vec![], |
| 65 | rnd: RefCell::new(StdRng::seed_from_u64(0)), |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | impl LogicalCounter { |
| 71 | #[must_use] |
| 72 | pub fn logical_resources(&self) -> LogicalResourceCounts { |
| 73 | LogicalResourceCounts { |
| 74 | num_qubits: self.next_free as _, |
| 75 | t_count: self.t_count as _, |
| 76 | rotation_count: self.r_count as _, |
| 77 | rotation_depth: self.layers.iter().filter(|layer| layer.r != 0).count() as _, |
| 78 | ccz_count: self.ccz_count as _, |
| 79 | ccix_count: 0, |
| 80 | measurement_count: self.m_count as _, |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | fn schedule_r(&mut self, q: usize) { |
| 85 | let level = self.level_at(q); |
| 86 | |
| 87 | if level == self.layers.len() { |
| 88 | self.layers.push(LayerInfo::new_with_r()); |
| 89 | } else { |
| 90 | self.layers[level].r += 1; |
| 91 | } |
| 92 | |
| 93 | self.max_layer[q] += 1; |
| 94 | } |
| 95 | |
| 96 | fn schedule_t(&mut self, q: usize) { |
| 97 | let level = self.level_at(q); |
| 98 | |
| 99 | if level == self.layers.len() { |
| 100 | self.layers.push(LayerInfo::new_with_t()); |
| 101 | } else { |
| 102 | self.layers[level].t += 1; |
| 103 | } |
| 104 | |
| 105 | self.max_layer[q] += 1; |
| 106 | } |
| 107 | |
| 108 | fn schedule_ccz(&mut self, q1: usize, q2: usize, q3: usize) { |
| 109 | let d1 = self.level_at(q1); |
| 110 | let d2 = self.level_at(q2); |
| 111 | let d3 = self.level_at(q3); |
| 112 | let max_depth = d1.max(d2).max(d3); |
| 113 | |
| 114 | if max_depth == self.layers.len() { |
| 115 | self.layers.push(LayerInfo::new_with_ccz()); |
| 116 | } else { |
| 117 | self.layers[max_depth].ccz += 1; |
| 118 | } |
| 119 | |
| 120 | self.max_layer[q1] = max_depth + 1; |
| 121 | self.max_layer[q2] = max_depth + 1; |
| 122 | self.max_layer[q3] = max_depth + 1; |
| 123 | } |
| 124 | |
| 125 | fn schedule_two_qubit_clifford(&mut self, q1: usize, q2: usize) { |
| 126 | let d1 = self.level_at(q1); |
| 127 | let d2 = self.level_at(q2); |
| 128 | let max_depth = d1.max(d2); |
| 129 | self.max_layer[q1] = max_depth; |
| 130 | self.max_layer[q2] = max_depth; |
| 131 | } |
| 132 | |
| 133 | fn level_at(&mut self, q: usize) -> usize { |
| 134 | while self.max_layer.len() <= q { |
| 135 | self.qubit_allocate(); |
| 136 | } |
| 137 | |
| 138 | self.max_layer[q] |
| 139 | } |
| 140 | |
| 141 | fn global_barrier(&mut self) -> usize { |
| 142 | let depth = self.layers.len(); |
| 143 | |
| 144 | for layer in &mut self.max_layer { |
| 145 | *layer = depth; |
| 146 | } |
| 147 | |
| 148 | self.allocation_barrier = depth; |
| 149 | depth |
| 150 | } |
| 151 | |
| 152 | fn begin_caching(&mut self, name: &str, variant: i64) -> bool { |
| 153 | let label = format!("{name}-{variant}"); |
| 154 | |
| 155 | if let Some(LayerCache::End { |
| 156 | start_depth, |
| 157 | end_depth, |
| 158 | combined_layer, |
| 159 | m_count, |
| 160 | }) = self.caching_layers.get(&label) |
| 161 | { |
| 162 | self.layers.extend_from_within(*start_depth..*end_depth); |
| 163 | |
| 164 | self.t_count += combined_layer.t; |
| 165 | self.r_count += combined_layer.r; |
| 166 | self.ccz_count += combined_layer.ccz; |
| 167 | self.m_count += *m_count; |
| 168 | |
| 169 | false |
| 170 | } else { |
| 171 | let depth = self.global_barrier(); |
| 172 | |
| 173 | self.caching_layers.insert( |
| 174 | label.clone(), |
| 175 | LayerCache::Begin { |
| 176 | start_depth: depth, |
| 177 | m_count: self.m_count, |
| 178 | }, |
| 179 | ); |
| 180 | self.caching_stack.push(label); |
| 181 | |
| 182 | true |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | fn end_caching(&mut self) -> Result<(), String> { |
| 187 | let Some(label) = self.caching_stack.pop() else { |
| 188 | return Err("cannot end caching before beginning caching".to_string()); |
| 189 | }; |
| 190 | |
| 191 | let entry = self |
| 192 | .caching_layers |
| 193 | .remove(&label) |
| 194 | .expect("layer caching should always have matching begin and end"); |
| 195 | |
| 196 | let LayerCache::Begin { |
| 197 | start_depth, |
| 198 | m_count, |
| 199 | } = entry |
| 200 | else { |
| 201 | panic!("layer caching should always have matching begin and end"); |
| 202 | }; |
| 203 | |
| 204 | let end_depth = self.layers.len(); |
| 205 | |
| 206 | let range = &self.layers[start_depth..end_depth]; |
| 207 | let sum: LayerInfo = range.iter().sum(); |
| 208 | |
| 209 | self.caching_layers.insert( |
| 210 | label, |
| 211 | LayerCache::End { |
| 212 | start_depth, |
| 213 | end_depth, |
| 214 | combined_layer: sum, |
| 215 | m_count: self.m_count - m_count, |
| 216 | }, |
| 217 | ); |
| 218 | |
| 219 | self.global_barrier(); |
| 220 | Ok(()) |
| 221 | } |
| 222 | |
| 223 | pub fn begin_repeat(&mut self, count: i64) -> Result<(), String> { |
| 224 | let start_depth = self.global_barrier(); |
| 225 | |
| 226 | self.repeats.push(RepeatEntry { |
| 227 | count: count |
| 228 | .try_into() |
| 229 | .map_err(|_| format!("Estimate count {count} is too large to fit in a usize.",))?, |
| 230 | start_depth, |
| 231 | m_count: self.m_count, |
| 232 | }); |
| 233 | |
| 234 | Ok(()) |
| 235 | } |
| 236 | |
| 237 | #[allow(clippy::similar_names)] |
| 238 | pub fn end_repeat(&mut self) { |
| 239 | if let Some(RepeatEntry { |
| 240 | count, |
| 241 | start_depth, |
| 242 | m_count, |
| 243 | }) = self.repeats.pop() |
| 244 | { |
| 245 | if count == 0 { |
| 246 | return; |
| 247 | } |
| 248 | |
| 249 | let end_depth = self.global_barrier(); |
| 250 | |
| 251 | let range = &self.layers[start_depth..end_depth]; |
| 252 | let sum: LayerInfo = range.iter().sum(); |
| 253 | |
| 254 | // We skip one iteration, which was already done explicitly between |
| 255 | // begin_repeat and end_repeat |
| 256 | let r_depth = range.iter().filter(|l| l.r != 0).count(); |
| 257 | let combined_r_depth = r_depth * (count - 1); |
| 258 | let combined_t_count = sum.t * (count - 1); |
| 259 | let combined_r_count = sum.r * (count - 1); |
| 260 | let combined_ccz_count = sum.ccz * (count - 1); |
| 261 | let combined_m_count = (self.m_count - m_count) * (count - 1); |
| 262 | |
| 263 | if r_depth > 0 { |
| 264 | let first_layer_r_count = combined_r_count - (combined_r_depth - 1); |
| 265 | |
| 266 | self.layers.push(LayerInfo { |
| 267 | ccz: combined_ccz_count, |
| 268 | r: first_layer_r_count, |
| 269 | t: combined_t_count, |
| 270 | }); |
| 271 | for _ in 1..combined_r_depth { |
| 272 | self.layers.push(LayerInfo::new_with_r()); |
| 273 | } |
| 274 | } else { |
| 275 | self.layers.push(LayerInfo { |
| 276 | ccz: combined_ccz_count, |
| 277 | r: combined_r_count, |
| 278 | t: combined_t_count, |
| 279 | }); |
| 280 | } |
| 281 | |
| 282 | self.t_count += combined_t_count; |
| 283 | self.r_count += combined_r_count; |
| 284 | self.ccz_count += combined_ccz_count; |
| 285 | self.m_count += combined_m_count; |
| 286 | |
| 287 | self.global_barrier(); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | fn add_estimate( |
| 292 | &mut self, |
| 293 | estimates: &[(i64, i64)], |
| 294 | layout: i64, |
| 295 | qubits: &[usize], |
| 296 | ) -> Result<(), String> { |
| 297 | if layout != 1 { |
| 298 | return Err( |
| 299 | "Parameter layout in AccountForEstimates must be 1 for PSSPCLayout.".to_string(), |
| 300 | ); |
| 301 | } |
| 302 | |
| 303 | let mut aux_qubit_count = 0_usize; |
| 304 | let mut t_count = 0_usize; |
| 305 | let mut r_count = 0_usize; |
| 306 | let mut r_depth = 0_usize; |
| 307 | let mut ccz_count = 0_usize; |
| 308 | let mut m_count = 0_usize; |
| 309 | for (kind, count) in estimates { |
| 310 | if *count < 0 { |
| 311 | return Err(format!("Negative estimate count: {count}")); |
| 312 | } |
| 313 | let count: usize = (*count) |
| 314 | .try_into() |
| 315 | .map_err(|_| format!("Estimate count {count} is too large to fit in a usize.",))?; |
| 316 | match *kind { |
| 317 | 0 => aux_qubit_count += count, |
| 318 | 1 => t_count += count, |
| 319 | 2 => r_count += count, |
| 320 | 3 => r_depth += count, |
| 321 | 4 => ccz_count += count, |
| 322 | 5 => m_count += count, |
| 323 | _ => return Err(format!("Unknown estimate kind: {kind}")), |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | // Allocate helper qubits |
| 328 | let helper_qubits = (0..aux_qubit_count) |
| 329 | .map(|_| self.qubit_allocate()) |
| 330 | .collect::<Vec<_>>(); |
| 331 | |
| 332 | // Set barrier among all qubits |
| 333 | let all_qubits = qubits.iter().chain(helper_qubits.iter()); |
| 334 | let max_depth = all_qubits |
| 335 | .clone() |
| 336 | .map(|q| self.max_layer[*q]) |
| 337 | .max() |
| 338 | .unwrap_or(0); |
| 339 | for qubit in all_qubits { |
| 340 | self.max_layer[*qubit] = max_depth; |
| 341 | } |
| 342 | |
| 343 | // Add up the estimates, dividing up between layers if appropriate. |
| 344 | let num_layers = if r_depth == 0 { |
| 345 | if r_count != 0 { |
| 346 | return Err("Rotation depth of zero must use rotation count zero.".to_string()); |
| 347 | } |
| 348 | |
| 349 | self.layers.push(LayerInfo { |
| 350 | t: t_count, |
| 351 | r: r_count, |
| 352 | ccz: ccz_count, |
| 353 | }); |
| 354 | |
| 355 | 1 |
| 356 | } else { |
| 357 | if r_depth < (r_count as f64 / qubits.len() as f64).ceil() as usize { |
| 358 | return Err(format!( |
| 359 | "Rotation depth {r_depth} is too small for rotation count {r_count} and {} qubits.", |
| 360 | qubits.len() |
| 361 | )); |
| 362 | } |
| 363 | |
| 364 | let r_count_per_layer = r_count / r_depth; |
| 365 | let extra_count = r_count - (r_count_per_layer * r_depth); |
| 366 | |
| 367 | self.layers.push(LayerInfo { |
| 368 | t: t_count, |
| 369 | r: r_count_per_layer + extra_count, |
| 370 | ccz: ccz_count, |
| 371 | }); |
| 372 | |
| 373 | for _ in 1..r_depth { |
| 374 | self.layers.push(LayerInfo { |
| 375 | t: 0, |
| 376 | r: r_count_per_layer, |
| 377 | ccz: 0, |
| 378 | }); |
| 379 | } |
| 380 | |
| 381 | r_depth |
| 382 | }; |
| 383 | |
| 384 | self.t_count += t_count; |
| 385 | self.r_count += r_count; |
| 386 | self.ccz_count += ccz_count; |
| 387 | self.m_count += m_count; |
| 388 | |
| 389 | for qubit in qubits { |
| 390 | self.max_layer[*qubit] += num_layers; |
| 391 | } |
| 392 | |
| 393 | // Release helper qubits |
| 394 | for qubit in helper_qubits { |
| 395 | self.qubit_release(qubit); |
| 396 | } |
| 397 | |
| 398 | Ok(()) |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | impl Backend for LogicalCounter { |
| 403 | type ResultType = bool; |
| 404 | |
| 405 | fn ccx(&mut self, ctl0: usize, ctl1: usize, q: usize) { |
| 406 | self.ccz_count += 1; |
| 407 | self.schedule_ccz(ctl0, ctl1, q); |
| 408 | } |
| 409 | |
| 410 | fn cx(&mut self, ctl: usize, q: usize) { |
| 411 | self.schedule_two_qubit_clifford(ctl, q); |
| 412 | } |
| 413 | |
| 414 | fn cy(&mut self, ctl: usize, q: usize) { |
| 415 | self.schedule_two_qubit_clifford(ctl, q); |
| 416 | } |
| 417 | |
| 418 | fn cz(&mut self, ctl: usize, q: usize) { |
| 419 | self.schedule_two_qubit_clifford(ctl, q); |
| 420 | } |
| 421 | |
| 422 | fn h(&mut self, _q: usize) {} |
| 423 | |
| 424 | fn m(&mut self, _q: usize) -> Self::ResultType { |
| 425 | self.m_count += 1; |
| 426 | |
| 427 | self.rnd.borrow_mut().gen_bool(0.5) |
| 428 | } |
| 429 | |
| 430 | fn mresetz(&mut self, q: usize) -> Self::ResultType { |
| 431 | self.m(q) |
| 432 | } |
| 433 | |
| 434 | fn reset(&mut self, _q: usize) {} |
| 435 | |
| 436 | fn rx(&mut self, theta: f64, q: usize) { |
| 437 | self.rz(theta, q); |
| 438 | } |
| 439 | |
| 440 | fn rxx(&mut self, theta: f64, q0: usize, q1: usize) { |
| 441 | self.rzz(theta, q0, q1); |
| 442 | } |
| 443 | |
| 444 | fn ry(&mut self, theta: f64, q: usize) { |
| 445 | self.rz(theta, q); |
| 446 | } |
| 447 | |
| 448 | fn ryy(&mut self, theta: f64, q0: usize, q1: usize) { |
| 449 | self.rzz(theta, q0, q1); |
| 450 | } |
| 451 | |
| 452 | fn rz(&mut self, theta: f64, q: usize) { |
| 453 | let multiple = (theta / (PI / 4.0)).round(); |
| 454 | if ((multiple * (PI / 4.0)) - theta).abs() <= f64::EPSILON { |
| 455 | let multiple = (multiple as i64).rem_euclid(8) as u64; |
| 456 | if multiple & 1 == 1 { |
| 457 | self.t(q); |
| 458 | } |
| 459 | } else { |
| 460 | self.r_count += 1; |
| 461 | self.schedule_r(q); |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | fn rzz(&mut self, theta: f64, q0: usize, q1: usize) { |
| 466 | self.cx(q1, q0); |
| 467 | self.rz(theta, q0); |
| 468 | self.cx(q1, q0); |
| 469 | } |
| 470 | |
| 471 | fn sadj(&mut self, _q: usize) {} |
| 472 | |
| 473 | fn s(&mut self, _q: usize) {} |
| 474 | |
| 475 | fn sx(&mut self, _q: usize) {} |
| 476 | |
| 477 | fn swap(&mut self, q0: usize, q1: usize) { |
| 478 | self.schedule_two_qubit_clifford(q0, q1); |
| 479 | } |
| 480 | |
| 481 | fn tadj(&mut self, q: usize) { |
| 482 | self.t_count += 1; |
| 483 | self.schedule_t(q); |
| 484 | } |
| 485 | |
| 486 | fn t(&mut self, q: usize) { |
| 487 | self.t_count += 1; |
| 488 | self.schedule_t(q); |
| 489 | } |
| 490 | |
| 491 | fn x(&mut self, _q: usize) {} |
| 492 | |
| 493 | fn y(&mut self, _q: usize) {} |
| 494 | |
| 495 | fn z(&mut self, _q: usize) {} |
| 496 | |
| 497 | fn qubit_allocate(&mut self) -> usize { |
| 498 | if let Some(index) = self.free_list.pop() { |
| 499 | index |
| 500 | } else { |
| 501 | let index = self.next_free; |
| 502 | self.next_free += 1; |
| 503 | self.max_layer.push(self.allocation_barrier); |
| 504 | index |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | fn qubit_release(&mut self, q: usize) -> bool { |
| 509 | self.free_list.push(q); |
| 510 | true |
| 511 | } |
| 512 | |
| 513 | fn qubit_swap_id(&mut self, _q0: usize, _q1: usize) { |
| 514 | // This can safely be treated as a no-op, because counts don't care which qubit is operated on, |
| 515 | // just how many operations are performed, and relabeling is non-physical. |
| 516 | } |
| 517 | |
| 518 | fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) { |
| 519 | (Vec::new(), 0) |
| 520 | } |
| 521 | |
| 522 | fn qubit_is_zero(&mut self, _q: usize) -> bool { |
| 523 | true |
| 524 | } |
| 525 | |
| 526 | fn custom_intrinsic(&mut self, name: &str, arg: Value) -> Option<Result<Value, String>> { |
| 527 | match name { |
| 528 | "BeginEstimateCaching" => { |
| 529 | let values = arg.unwrap_tuple(); |
| 530 | let [cache_name, cache_variant] = array::from_fn(|i| values[i].clone()); |
| 531 | Some(Ok(Value::Bool(self.begin_caching( |
| 532 | &cache_name.unwrap_string(), |
| 533 | cache_variant.unwrap_int(), |
| 534 | )))) |
| 535 | } |
| 536 | "EndEstimateCaching" => Some(self.end_caching().map(|()| Value::unit())), |
| 537 | "BeginRepeatEstimatesInternal" => { |
| 538 | let count = arg.unwrap_int(); |
| 539 | Some(self.begin_repeat(count).map(|()| Value::unit())) |
| 540 | } |
| 541 | "EndRepeatEstimatesInternal" => { |
| 542 | self.end_repeat(); |
| 543 | Some(Ok(Value::unit())) |
| 544 | } |
| 545 | "AccountForEstimatesInternal" => { |
| 546 | let values = arg.unwrap_tuple(); |
| 547 | let [estimates, layout, qubits] = array::from_fn(|i| values[i].clone()); |
| 548 | let estimates = estimates |
| 549 | .unwrap_array() |
| 550 | .iter() |
| 551 | .map(|v| { |
| 552 | let entry = v.clone().unwrap_tuple(); |
| 553 | let [variant, count] = array::from_fn(|i| entry[i].clone()); |
| 554 | let variant = variant.unwrap_int(); |
| 555 | let count = count.unwrap_int(); |
| 556 | (variant, count) |
| 557 | }) |
| 558 | .collect::<Vec<_>>(); |
| 559 | let layout = layout.unwrap_int(); |
| 560 | let qubits = qubits |
| 561 | .unwrap_array() |
| 562 | .iter() |
| 563 | .map(|v| v.clone().unwrap_qubit().deref().0) |
| 564 | .collect::<Vec<_>>(); |
| 565 | Some( |
| 566 | self.add_estimate(&estimates, layout, &qubits) |
| 567 | .map(|()| Value::unit()), |
| 568 | ) |
| 569 | } |
| 570 | "GlobalPhase" | "ConfigurePauliNoise" | "ConfigureQubitLoss" | "ApplyIdleNoise" => { |
| 571 | Some(Ok(Value::unit())) |
| 572 | } |
| 573 | _ => None, |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | #[derive(Default, Debug, Clone, PartialEq, Eq)] |
| 579 | pub struct LayerInfo { |
| 580 | t: usize, |
| 581 | r: usize, |
| 582 | ccz: usize, |
| 583 | } |
| 584 | |
| 585 | impl LayerInfo { |
| 586 | #[must_use] |
| 587 | pub fn new_with_t() -> Self { |
| 588 | Self { t: 1, r: 0, ccz: 0 } |
| 589 | } |
| 590 | |
| 591 | #[must_use] |
| 592 | pub fn new_with_r() -> Self { |
| 593 | Self { t: 0, r: 1, ccz: 0 } |
| 594 | } |
| 595 | |
| 596 | #[must_use] |
| 597 | pub fn new_with_ccz() -> Self { |
| 598 | Self { t: 0, r: 0, ccz: 1 } |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | impl<'a> Sum<&'a LayerInfo> for LayerInfo { |
| 603 | fn sum<I: Iterator<Item = &'a LayerInfo>>(iter: I) -> Self { |
| 604 | let mut layer = LayerInfo::default(); |
| 605 | |
| 606 | for current in iter { |
| 607 | layer.t += current.t; |
| 608 | layer.r += current.r; |
| 609 | layer.ccz += current.ccz; |
| 610 | } |
| 611 | |
| 612 | layer |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | enum LayerCache { |
| 617 | Begin { |
| 618 | start_depth: usize, |
| 619 | m_count: usize, |
| 620 | }, |
| 621 | End { |
| 622 | start_depth: usize, |
| 623 | end_depth: usize, |
| 624 | combined_layer: LayerInfo, |
| 625 | m_count: usize, |
| 626 | }, |
| 627 | } |
| 628 | |
| 629 | struct RepeatEntry { |
| 630 | count: usize, |
| 631 | start_depth: usize, |
| 632 | m_count: usize, |
| 633 | } |
| 634 | |