1use std::cmp::Reverse;
7use std::collections::BinaryHeap;
8
9use super::DetectorErrorModel;
10use super::decoder::{
11 BOUNDARY, GraphEdges, ShotDecoder, Stuck, decode_batch, graphlike_edges, logical_error_rate,
12};
13use crate::error::{PrismError, Result};
14use crate::sim::compiled::PackedShots;
15
16const NONE: u32 = u32::MAX;
17const DEAD: u32 = u32::MAX - 1;
18const INF: i64 = i64::MAX / 4;
19const WEIGHT_LEVELS: f64 = (1u64 << 23) as f64;
22
23const EVENT_FRONTIER: u8 = 0;
24const EVENT_PAIR: u8 = 1;
25const EVENT_BOUNDARY: u8 = 2;
26const EVENT_EXPAND: u8 = 3;
27
28#[derive(Debug, Clone)]
42pub struct MatchingDecoder {
43 num_detectors: usize,
44 num_observables: usize,
45 obs_words: usize,
46 adj_offsets: Vec<u32>,
47 adj_target: Vec<u32>,
48 adj_weight: Vec<i64>,
49 adj_edge: Vec<u32>,
50 edge_obs: Vec<u64>,
51 boundary_dist: Vec<i64>,
52 boundary_obs: Vec<u64>,
53}
54
55impl MatchingDecoder {
56 pub fn from_model(model: &DetectorErrorModel) -> Result<Self> {
65 Ok(Self::from_graph(graphlike_edges(model, "matching")?))
66 }
67
68 fn from_graph(graph: GraphEdges) -> Self {
69 let GraphEdges {
70 num_detectors,
71 num_observables,
72 edge_u,
73 edge_v,
74 edge_p,
75 edge_obs,
76 } = graph;
77 let obs_words = num_observables.div_ceil(64);
78 let weights = quantized_weights(&edge_p);
79
80 let mut adj_offsets = vec![0u32; num_detectors + 1];
81 let mut boundary_edge = vec![NONE; num_detectors];
82 for edge in 0..edge_u.len() {
83 if edge_v[edge] == BOUNDARY {
84 boundary_edge[edge_u[edge] as usize] = edge as u32;
85 } else {
86 adj_offsets[edge_u[edge] as usize + 1] += 1;
87 adj_offsets[edge_v[edge] as usize + 1] += 1;
88 }
89 }
90 for v in 0..num_detectors {
91 adj_offsets[v + 1] += adj_offsets[v];
92 }
93 let slots = *adj_offsets.last().unwrap() as usize;
94 let mut cursor = adj_offsets.clone();
95 let mut adj_target = vec![0u32; slots];
96 let mut adj_weight = vec![0i64; slots];
97 let mut adj_edge = vec![0u32; slots];
98 for edge in 0..edge_u.len() {
99 if edge_v[edge] == BOUNDARY {
100 continue;
101 }
102 for (from, to) in [(edge_u[edge], edge_v[edge]), (edge_v[edge], edge_u[edge])] {
103 let at = cursor[from as usize] as usize;
104 adj_target[at] = to;
105 adj_weight[at] = weights[edge];
106 adj_edge[at] = edge as u32;
107 cursor[from as usize] += 1;
108 }
109 }
110
111 let mut boundary_dist = vec![INF; num_detectors];
115 let mut boundary_obs = vec![0u64; num_detectors * obs_words];
116 let mut settled = vec![false; num_detectors];
117 let mut heap = Frontier::new();
118 for (v, &edge) in boundary_edge.iter().enumerate() {
119 if edge != NONE {
120 heap.push(Reverse((weights[edge as usize], v as u32, NONE, edge)));
121 }
122 }
123 while let Some(Reverse((dist, v, from, edge))) = heap.pop() {
124 let at = v as usize;
125 if settled[at] {
126 continue;
127 }
128 settled[at] = true;
129 boundary_dist[at] = dist;
130 let edge_row = &edge_obs[edge as usize * obs_words..(edge as usize + 1) * obs_words];
131 for word in 0..obs_words {
132 let base = if from == NONE {
133 0
134 } else {
135 boundary_obs[from as usize * obs_words + word]
136 };
137 boundary_obs[at * obs_words + word] = base ^ edge_row[word];
138 }
139 for slot in adj_offsets[at] as usize..adj_offsets[at + 1] as usize {
140 let next = adj_target[slot];
141 if !settled[next as usize] {
142 heap.push(Reverse((dist + adj_weight[slot], next, v, adj_edge[slot])));
143 }
144 }
145 }
146
147 Self {
148 num_detectors,
149 num_observables,
150 obs_words,
151 adj_offsets,
152 adj_target,
153 adj_weight,
154 adj_edge,
155 edge_obs,
156 boundary_dist,
157 boundary_obs,
158 }
159 }
160
161 pub fn num_detectors(&self) -> usize {
162 self.num_detectors
163 }
164
165 pub fn num_observables(&self) -> usize {
166 self.num_observables
167 }
168
169 pub fn decode_packed(&self, detectors: &PackedShots) -> Result<PackedShots> {
179 decode_batch(self, detectors)
180 }
181
182 pub fn logical_error_rate(
190 &self,
191 detectors: &PackedShots,
192 observables: &PackedShots,
193 ) -> Result<f64> {
194 logical_error_rate(&self.decode_packed(detectors)?, observables)
195 }
196
197 fn match_shot(
200 &self,
201 row: &[u64],
202 out_row: &mut [u64],
203 s: &mut MatchScratch,
204 ) -> std::result::Result<i64, Stuck> {
205 s.begin_shot();
206 for (word_index, &bits) in row.iter().enumerate() {
207 let mut bits = bits;
208 while bits != 0 {
209 s.defects
210 .push((word_index * 64) as u32 + bits.trailing_zeros());
211 bits &= bits - 1;
212 }
213 }
214 let k = s.defects.len();
215 if k == 0 {
216 return Ok(0);
217 }
218 s.prepare(k);
219 for defect in 0..k as u32 {
220 let vertex = s.defects[defect as usize];
221 self.settle(s, defect, vertex, 0, NONE, NONE);
222 }
223 for defect in 0..k as u32 {
224 self.schedule_growth(s, defect);
225 }
226
227 while s.roots > 0 {
228 let Some(Reverse(event)) = s.events.pop() else {
229 return Err(s.stuck());
230 };
231 debug_assert!(event.time >= s.now);
232 s.now = event.time;
233 match event.kind {
234 EVENT_FRONTIER => self.on_frontier(s, event.id),
235 EVENT_PAIR => self.on_pair(s, event.id),
236 EVENT_BOUNDARY => self.on_boundary(s, event.id),
237 _ => self.on_expand(s, event.id),
238 }
239 }
240 Ok(self.extract(s, out_row))
241 }
242
243 fn settle(
246 &self,
247 s: &mut MatchScratch,
248 defect: u32,
249 vertex: u32,
250 dist: i64,
251 prev: u32,
252 via: u32,
253 ) {
254 let at = vertex as usize;
255 if s.vertex_stamp[at] != s.stamp {
256 s.vertex_stamp[at] = s.stamp;
257 s.vertex_head[at] = NONE;
258 }
259 let entry = s.entries.len() as u32;
260 let mut other = s.vertex_head[at];
261 while other != NONE {
262 let met = s.entries[other as usize];
263 s.offer(defect, entry, met.defect, other, dist + met.dist, NONE);
264 other = met.next;
265 }
266 s.entries.push(BallEntry {
267 defect,
268 dist,
269 prev,
270 via,
271 next: s.vertex_head[at],
272 });
273 s.vertex_head[at] = entry;
274
275 for slot in self.adj_offsets[at] as usize..self.adj_offsets[at + 1] as usize {
276 let next = self.adj_target[slot];
277 let reach = dist + self.adj_weight[slot];
278 let mut reached = false;
279 if s.vertex_stamp[next as usize] == s.stamp {
280 let mut other = s.vertex_head[next as usize];
281 while other != NONE {
282 let met = s.entries[other as usize];
283 if met.defect == defect {
284 reached = true;
285 } else {
286 s.offer(
287 defect,
288 entry,
289 met.defect,
290 other,
291 reach + met.dist,
292 self.adj_edge[slot],
293 );
294 }
295 other = met.next;
296 }
297 }
298 if !reached {
299 s.heaps[defect as usize].push(Reverse((reach, next, entry, self.adj_edge[slot])));
300 }
301 }
302 }
303
304 fn schedule_growth(&self, s: &mut MatchScratch, defect: u32) {
305 let radius = s.radius(defect);
306 if let Some(&Reverse((dist, ..))) = s.heaps[defect as usize].peek() {
307 s.push_event((dist - radius).max(0), EVENT_FRONTIER, defect);
308 }
309 let boundary = self.boundary_dist[s.defects[defect as usize] as usize];
310 if boundary < INF {
311 debug_assert!(boundary >= radius);
312 s.push_event(boundary - radius, EVENT_BOUNDARY, defect);
313 }
314 }
315
316 fn reschedule(&self, s: &mut MatchScratch, node: u32) {
317 let label = s.nodes[node as usize].label;
318 let tail = s.nodes[node as usize].tail;
319 let mut defect = s.nodes[node as usize].head;
320 loop {
321 for at in 0..s.incident[defect as usize].len() {
322 let pair = s.incident[defect as usize][at];
323 s.schedule_pair(pair);
324 }
325 if label == Label::S {
326 self.schedule_growth(s, defect);
327 }
328 if defect == tail {
329 break;
330 }
331 defect = s.defect_next[defect as usize];
332 }
333 if label == Label::T && node as usize >= s.defects.len() {
334 let delay = s.dual(node);
335 s.push_event(delay, EVENT_EXPAND, node);
336 }
337 }
338
339 fn on_frontier(&self, s: &mut MatchScratch, defect: u32) {
340 if s.nodes[s.top[defect as usize] as usize].label != Label::S {
341 return;
342 }
343 let radius = s.radius(defect);
344 while let Some(&Reverse((dist, vertex, prev, via))) = s.heaps[defect as usize].peek() {
345 if dist > radius {
346 break;
347 }
348 s.heaps[defect as usize].pop();
349 if s.vertex_stamp[vertex as usize] == s.stamp {
350 let mut other = s.vertex_head[vertex as usize];
351 let mut reached = false;
352 while other != NONE {
353 let met = s.entries[other as usize];
354 if met.defect == defect {
355 reached = true;
356 break;
357 }
358 other = met.next;
359 }
360 if reached {
361 continue;
362 }
363 }
364 self.settle(s, defect, vertex, dist, prev, via);
365 }
366 if let Some(&Reverse((dist, ..))) = s.heaps[defect as usize].peek() {
367 s.push_event(dist - radius, EVENT_FRONTIER, defect);
368 }
369 }
370
371 fn on_pair(&self, s: &mut MatchScratch, pair: u32) {
372 let Pair { a, b, cand, .. } = s.pairs[pair as usize];
373 let (x, y) = (s.top[a as usize], s.top[b as usize]);
374 if x == y || cand - s.radius(a) - s.radius(b) != 0 {
375 return;
376 }
377 match (s.nodes[x as usize].label, s.nodes[y as usize].label) {
378 (Label::S, Label::S) => {
379 if s.nodes[x as usize].tree == s.nodes[y as usize].tree {
380 self.form_blossom(s, x, y, pair);
381 } else {
382 self.augment(s, x, y, pair);
383 }
384 }
385 (Label::S, Label::Frozen) => self.hit_frozen(s, x, y, pair),
386 (Label::Frozen, Label::S) => self.hit_frozen(s, y, x, pair),
387 _ => {}
388 }
389 }
390
391 fn on_boundary(&self, s: &mut MatchScratch, defect: u32) {
392 let x = s.top[defect as usize];
393 if s.nodes[x as usize].label != Label::S
394 || s.radius(defect) != self.boundary_dist[s.defects[defect as usize] as usize]
395 {
396 return;
397 }
398 let tree = s.nodes[x as usize].tree;
399 s.augment_path(x);
400 s.nodes[x as usize].mate = Mate::Boundary(defect);
401 self.dissolve(s, tree);
402 }
403
404 fn on_expand(&self, s: &mut MatchScratch, node: u32) {
405 let n = &s.nodes[node as usize];
406 if n.blossom_parent != NONE || n.label != Label::T || s.dual(node) != 0 {
407 return;
408 }
409 self.expand(s, node);
410 }
411
412 fn augment(&self, s: &mut MatchScratch, x: u32, y: u32, pair: u32) {
413 let (tx, ty) = (s.nodes[x as usize].tree, s.nodes[y as usize].tree);
414 s.augment_path(x);
415 s.augment_path(y);
416 s.nodes[x as usize].mate = Mate::Pair(pair);
417 s.nodes[y as usize].mate = Mate::Pair(pair);
418 self.dissolve(s, tx);
419 self.dissolve(s, ty);
420 }
421
422 fn hit_frozen(&self, s: &mut MatchScratch, x: u32, y: u32, pair: u32) {
426 let tree = s.nodes[x as usize].tree;
427 match s.nodes[y as usize].mate {
428 Mate::Pair(mate_pair) => {
429 let z = s.other_top(mate_pair, y);
430 s.set_slope(y, -1);
431 s.set_slope(z, 1);
432 let ny = &mut s.nodes[y as usize];
433 ny.label = Label::T;
434 ny.tree = tree;
435 ny.parent = pair;
436 let nz = &mut s.nodes[z as usize];
437 nz.label = Label::S;
438 nz.tree = tree;
439 nz.parent = mate_pair;
440 s.trees[tree as usize].push(y);
441 s.trees[tree as usize].push(z);
442 self.reschedule(s, y);
443 self.reschedule(s, z);
444 }
445 Mate::Boundary(_) => {
446 s.augment_path(x);
447 s.nodes[x as usize].mate = Mate::Pair(pair);
448 s.nodes[y as usize].mate = Mate::Pair(pair);
449 self.dissolve(s, tree);
450 }
451 Mate::None => unreachable!("out-of-tree nodes are matched"),
452 }
453 }
454
455 fn dissolve(&self, s: &mut MatchScratch, tree: u32) {
456 s.roots -= 1;
457 let mut members = std::mem::take(&mut s.trees[tree as usize]);
458 members.retain(|&node| {
459 let n = &s.nodes[node as usize];
460 n.blossom_parent == NONE && n.tree == tree
461 });
462 for &node in &members {
463 s.set_slope(node, 0);
464 let n = &mut s.nodes[node as usize];
465 n.label = Label::Frozen;
466 n.tree = NONE;
467 n.parent = NONE;
468 }
469 for &node in &members {
470 self.reschedule(s, node);
471 }
472 members.clear();
473 s.trees[tree as usize] = members;
474 }
475
476 fn form_blossom(&self, s: &mut MatchScratch, x: u32, y: u32, pair: u32) {
477 if s.mark.len() < s.nodes.len() {
478 s.mark.resize(s.nodes.len(), 0);
479 }
480 s.mark_stamp += 1;
481 let stamp = s.mark_stamp;
482 let mut n = x;
483 loop {
484 s.mark[n as usize] = stamp;
485 if s.nodes[n as usize].parent == NONE {
486 break;
487 }
488 n = s.parent_node(n);
489 }
490 s.path_y.clear();
491 let mut n = y;
492 while s.mark[n as usize] != stamp {
493 s.path_y.push(n);
494 n = s.parent_node(n);
495 }
496 let lca = n;
497 s.path_x.clear();
498 let mut n = x;
499 while n != lca {
500 s.path_x.push(n);
501 n = s.parent_node(n);
502 }
503
504 let blossom = s.nodes.len() as u32;
505 let cycle_start = s.cycles.len() as u32;
506 let first_link = match s.path_x.last() {
507 Some(&top_x) => s.nodes[top_x as usize].parent,
508 None => pair,
509 };
510 s.cycles.push(CycleLink {
511 child: lca,
512 pair: first_link,
513 });
514 for at in (0..s.path_x.len()).rev() {
515 let child = s.path_x[at];
516 let link = if at > 0 {
517 s.nodes[s.path_x[at - 1] as usize].parent
518 } else {
519 pair
520 };
521 s.cycles.push(CycleLink { child, pair: link });
522 }
523 for at in 0..s.path_y.len() {
524 let child = s.path_y[at];
525 let link = s.nodes[child as usize].parent;
526 s.cycles.push(CycleLink { child, pair: link });
527 }
528 let cycle_len = s.cycles.len() as u32 - cycle_start;
529 debug_assert!(cycle_len % 2 == 1);
530
531 let base = &s.nodes[lca as usize];
532 let node = Node {
533 y: 0,
534 y_time: s.now,
535 slope: 1,
536 label: Label::S,
537 tree: base.tree,
538 parent: base.parent,
539 mate: base.mate,
540 blossom_parent: NONE,
541 cycle_start,
542 cycle_len,
543 head: base.head,
544 tail: s.nodes[s.cycles[(cycle_start + cycle_len - 1) as usize].child as usize].tail,
545 };
546 s.nodes.push(node);
547 for at in cycle_start..cycle_start + cycle_len {
548 let child = s.cycles[at as usize].child;
549 s.set_slope(child, 0);
550 let c = &mut s.nodes[child as usize];
551 c.blossom_parent = blossom;
552 c.label = Label::Frozen;
553 c.tree = NONE;
554 let (head, tail, dual) = (c.head, c.tail, c.y);
555 if at + 1 < cycle_start + cycle_len {
556 let next = s.cycles[at as usize + 1].child;
557 s.defect_next[tail as usize] = s.nodes[next as usize].head;
558 }
559 let mut defect = head;
560 loop {
561 s.inner[defect as usize] += dual;
562 s.top[defect as usize] = blossom;
563 if defect == tail {
564 break;
565 }
566 defect = s.defect_next[defect as usize];
567 }
568 }
569 let tree = s.nodes[blossom as usize].tree;
570 s.trees[tree as usize].push(blossom);
571 self.reschedule(s, blossom);
572 }
573
574 fn expand(&self, s: &mut MatchScratch, blossom: u32) {
579 let b = s.nodes[blossom as usize];
580 let Mate::Pair(out_pair) = b.mate else {
581 unreachable!("T blossoms are matched to their tree child");
582 };
583 let in_defect = s.endpoint_in(b.parent, blossom);
584 let out_defect = s.endpoint_in(out_pair, blossom);
585 let start = b.cycle_start as usize;
586 let len = b.cycle_len as usize;
587 for at in start..start + len {
588 let child = s.cycles[at].child;
589 let c = &mut s.nodes[child as usize];
590 c.blossom_parent = NONE;
591 let (head, tail, dual) = (c.head, c.tail, c.y);
592 let mut defect = head;
593 loop {
594 s.inner[defect as usize] -= dual;
595 s.top[defect as usize] = child;
596 if defect == tail {
597 break;
598 }
599 defect = s.defect_next[defect as usize];
600 }
601 }
602 s.nodes[blossom as usize].blossom_parent = DEAD;
603
604 let child_in = s.top[in_defect as usize];
605 let child_out = s.top[out_defect as usize];
606 let position = |s: &MatchScratch, child: u32| {
607 (0..len)
608 .find(|&at| s.cycles[start + at].child == child)
609 .unwrap()
610 };
611 let p_in = position(s, child_in);
612 let p_out = position(s, child_out);
613 let forward = (p_out + len - p_in) % len;
614 let (step, path_len) = if forward.is_multiple_of(2) {
615 (1, forward)
616 } else {
617 (len - 1, len - forward)
618 };
619 let link = |s: &MatchScratch, at: usize| {
621 if step == 1 {
622 s.cycles[start + at].pair
623 } else {
624 s.cycles[start + (at + len - 1) % len].pair
625 }
626 };
627
628 let tree = b.tree;
629 let mut at = p_in;
630 let mut prev_link = b.parent;
631 for k in 0..=path_len {
632 let child = s.cycles[start + at].child;
633 let next_link = if k == path_len { out_pair } else { link(s, at) };
634 let (label, slope, mate) = if k % 2 == 0 {
635 (Label::T, -1, next_link)
636 } else {
637 (Label::S, 1, prev_link)
638 };
639 s.set_slope(child, slope);
640 let c = &mut s.nodes[child as usize];
641 c.label = label;
642 c.tree = tree;
643 c.parent = prev_link;
644 c.mate = Mate::Pair(mate);
645 s.trees[tree as usize].push(child);
646 prev_link = next_link;
647 at = (at + step) % len;
648 }
649 for _ in 0..(len - path_len - 1) / 2 {
650 let pair_link = link(s, at);
651 let u = s.cycles[start + at].child;
652 at = (at + step) % len;
653 let v = s.cycles[start + at].child;
654 at = (at + step) % len;
655 for node in [u, v] {
656 s.set_slope(node, 0);
657 let c = &mut s.nodes[node as usize];
658 c.label = Label::Frozen;
659 c.tree = NONE;
660 c.parent = NONE;
661 c.mate = Mate::Pair(pair_link);
662 }
663 }
664 for at in start..start + len {
665 let child = s.cycles[at].child;
666 self.reschedule(s, child);
667 }
668 }
669
670 fn extract(&self, s: &mut MatchScratch, out_row: &mut [u64]) -> i64 {
673 let k = s.defects.len() as u32;
674 let mut weight = 0i64;
675 s.stack.clear();
676 for node in 0..s.nodes.len() as u32 {
677 let Node {
678 blossom_parent,
679 mate,
680 ..
681 } = s.nodes[node as usize];
682 if blossom_parent != NONE {
683 continue;
684 }
685 match mate {
686 Mate::Pair(pair) => {
687 let inside = s.endpoint_in(pair, node);
688 if inside == s.pairs[pair as usize].a {
689 weight += self.pair_flips(s, pair, out_row);
690 }
691 s.stack.push((node, inside));
692 }
693 Mate::Boundary(defect) => {
694 weight += self.boundary_flips(s, defect, out_row);
695 s.stack.push((node, defect));
696 }
697 Mate::None => unreachable!("every defect is matched when no tree is left"),
698 }
699 }
700 while let Some((node, outside_defect)) = s.stack.pop() {
701 if node < k {
702 continue;
703 }
704 let start = s.nodes[node as usize].cycle_start as usize;
705 let len = s.nodes[node as usize].cycle_len as usize;
706 let entry_child = s.child_holding(node, outside_defect);
707 let p = (0..len)
708 .find(|&at| s.cycles[start + at].child == entry_child)
709 .unwrap();
710 s.stack.push((entry_child, outside_defect));
711 for j in (1..len).step_by(2) {
712 let pair = s.cycles[start + (p + j) % len].pair;
713 let u = s.cycles[start + (p + j) % len].child;
714 let v = s.cycles[start + (p + j + 1) % len].child;
715 weight += self.pair_flips(s, pair, out_row);
716 let Pair { a, b, .. } = s.pairs[pair as usize];
717 let (in_u, in_v) = if s.child_holding(node, a) == u {
718 (a, b)
719 } else {
720 (b, a)
721 };
722 s.stack.push((u, in_u));
723 s.stack.push((v, in_v));
724 }
725 }
726 weight
727 }
728
729 fn pair_flips(&self, s: &MatchScratch, pair: u32, out_row: &mut [u64]) -> i64 {
730 let p = s.pairs[pair as usize];
731 if p.via != NONE {
732 self.xor_edge(p.via, out_row);
733 }
734 for start in [p.meet_a, p.meet_b] {
735 let mut entry = start;
736 while entry != NONE {
737 let e = s.entries[entry as usize];
738 if e.via != NONE {
739 self.xor_edge(e.via, out_row);
740 }
741 entry = e.prev;
742 }
743 }
744 p.cand
745 }
746
747 fn boundary_flips(&self, s: &MatchScratch, defect: u32, out_row: &mut [u64]) -> i64 {
748 let vertex = s.defects[defect as usize] as usize;
749 let row = &self.boundary_obs[vertex * self.obs_words..(vertex + 1) * self.obs_words];
750 for (word, mask) in out_row.iter_mut().zip(row) {
751 *word ^= mask;
752 }
753 self.boundary_dist[vertex]
754 }
755
756 #[inline]
757 fn xor_edge(&self, edge: u32, out_row: &mut [u64]) {
758 let base = edge as usize * self.obs_words;
759 for (word, mask) in out_row
760 .iter_mut()
761 .zip(&self.edge_obs[base..base + self.obs_words])
762 {
763 *word ^= mask;
764 }
765 }
766}
767
768impl ShotDecoder for MatchingDecoder {
769 type Scratch = MatchScratch;
770 type Failure = Stuck;
771
772 fn num_detectors(&self) -> usize {
773 self.num_detectors
774 }
775
776 fn num_observables(&self) -> usize {
777 self.num_observables
778 }
779
780 fn scratch(&self) -> MatchScratch {
781 MatchScratch::new(self.num_detectors)
782 }
783
784 fn decode_shot(
785 &self,
786 row: &[u64],
787 out_row: &mut [u64],
788 scratch: &mut MatchScratch,
789 ) -> std::result::Result<(), Stuck> {
790 self.match_shot(row, out_row, scratch).map(|_| ())
791 }
792
793 fn failure_error(failure: Stuck, shot: usize) -> PrismError {
794 failure.into_error(shot)
795 }
796}
797
798fn quantized_weights(edge_p: &[f64]) -> Vec<i64> {
801 let raw: Vec<f64> = edge_p
802 .iter()
803 .map(|&p| ((1.0 - p) / p).ln().max(0.0))
804 .collect();
805 let largest = raw.iter().copied().fold(0.0f64, f64::max);
806 let scale = if largest > 0.0 {
807 WEIGHT_LEVELS / largest
808 } else {
809 0.0
810 };
811 raw.iter()
812 .map(|&w| 2 * (w * scale).round() as i64)
813 .collect()
814}
815
816type Frontier = BinaryHeap<Reverse<(i64, u32, u32, u32)>>;
819
820#[derive(Debug, Clone, Copy)]
823struct BallEntry {
824 defect: u32,
825 dist: i64,
826 prev: u32,
827 via: u32,
828 next: u32,
829}
830
831#[derive(Debug, Clone, Copy)]
835struct Pair {
836 a: u32,
837 b: u32,
838 cand: i64,
839 meet_a: u32,
840 meet_b: u32,
841 via: u32,
842}
843
844#[derive(Debug, Clone, Copy, PartialEq, Eq)]
845enum Label {
846 S,
847 T,
848 Frozen,
849}
850
851#[derive(Debug, Clone, Copy)]
852enum Mate {
853 None,
854 Pair(u32),
855 Boundary(u32),
856}
857
858#[derive(Debug, Clone, Copy)]
863struct Node {
864 y: i64,
865 y_time: i64,
866 slope: i64,
867 label: Label,
868 tree: u32,
869 parent: u32,
870 mate: Mate,
871 blossom_parent: u32,
872 cycle_start: u32,
873 cycle_len: u32,
874 head: u32,
875 tail: u32,
876}
877
878#[derive(Debug, Clone, Copy)]
880struct CycleLink {
881 child: u32,
882 pair: u32,
883}
884
885#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
886struct Event {
887 time: i64,
888 kind: u8,
889 id: u32,
890}
891
892pub(super) struct MatchScratch {
896 stamp: u64,
897 vertex_stamp: Vec<u64>,
898 vertex_head: Vec<u32>,
899 entries: Vec<BallEntry>,
900 heaps: Vec<Frontier>,
901 defects: Vec<u32>,
902 pairs: Vec<Pair>,
903 incident: Vec<Vec<u32>>,
904 nodes: Vec<Node>,
905 cycles: Vec<CycleLink>,
906 defect_next: Vec<u32>,
907 inner: Vec<i64>,
908 top: Vec<u32>,
909 trees: Vec<Vec<u32>>,
910 events: BinaryHeap<Reverse<Event>>,
911 mark: Vec<u64>,
912 mark_stamp: u64,
913 path_x: Vec<u32>,
914 path_y: Vec<u32>,
915 stack: Vec<(u32, u32)>,
916 now: i64,
917 roots: usize,
918}
919
920impl MatchScratch {
921 fn new(num_detectors: usize) -> Self {
922 Self {
923 stamp: 0,
924 vertex_stamp: vec![0; num_detectors],
925 vertex_head: vec![NONE; num_detectors],
926 entries: Vec::new(),
927 heaps: Vec::new(),
928 defects: Vec::new(),
929 pairs: Vec::new(),
930 incident: Vec::new(),
931 nodes: Vec::new(),
932 cycles: Vec::new(),
933 defect_next: Vec::new(),
934 inner: Vec::new(),
935 top: Vec::new(),
936 trees: Vec::new(),
937 events: BinaryHeap::new(),
938 mark: Vec::new(),
939 mark_stamp: 0,
940 path_x: Vec::new(),
941 path_y: Vec::new(),
942 stack: Vec::new(),
943 now: 0,
944 roots: 0,
945 }
946 }
947
948 fn begin_shot(&mut self) {
949 self.stamp += 1;
950 self.defects.clear();
951 }
952
953 fn prepare(&mut self, k: usize) {
954 self.entries.clear();
955 self.pairs.clear();
956 self.nodes.clear();
957 self.cycles.clear();
958 self.events.clear();
959 self.defect_next.clear();
960 self.inner.clear();
961 self.top.clear();
962 if self.heaps.len() < k {
963 self.heaps.resize_with(k, BinaryHeap::new);
964 self.incident.resize_with(k, Vec::new);
965 self.trees.resize_with(k, Vec::new);
966 }
967 for defect in 0..k as u32 {
968 self.heaps[defect as usize].clear();
969 self.incident[defect as usize].clear();
970 self.trees[defect as usize].clear();
971 self.trees[defect as usize].push(defect);
972 self.nodes.push(Node {
973 y: 0,
974 y_time: 0,
975 slope: 1,
976 label: Label::S,
977 tree: defect,
978 parent: NONE,
979 mate: Mate::None,
980 blossom_parent: NONE,
981 cycle_start: 0,
982 cycle_len: 0,
983 head: defect,
984 tail: defect,
985 });
986 self.defect_next.push(NONE);
987 self.inner.push(0);
988 self.top.push(defect);
989 }
990 self.now = 0;
991 self.roots = k;
992 }
993
994 #[inline]
995 fn dual(&self, node: u32) -> i64 {
996 let n = &self.nodes[node as usize];
997 n.y + n.slope * (self.now - n.y_time)
998 }
999
1000 #[inline]
1002 fn radius(&self, defect: u32) -> i64 {
1003 self.inner[defect as usize] + self.dual(self.top[defect as usize])
1004 }
1005
1006 fn set_slope(&mut self, node: u32, slope: i64) {
1007 let y = self.dual(node);
1008 let now = self.now;
1009 let n = &mut self.nodes[node as usize];
1010 n.y = y;
1011 n.y_time = now;
1012 n.slope = slope;
1013 }
1014
1015 fn push_event(&mut self, delay: i64, kind: u8, id: u32) {
1016 self.events.push(Reverse(Event {
1017 time: self.now + delay,
1018 kind,
1019 id,
1020 }));
1021 }
1022
1023 fn offer(
1024 &mut self,
1025 defect: u32,
1026 entry: u32,
1027 other: u32,
1028 other_entry: u32,
1029 cand: i64,
1030 via: u32,
1031 ) {
1032 let found = self.incident[defect as usize]
1033 .iter()
1034 .copied()
1035 .find(|&pair| {
1036 let p = &self.pairs[pair as usize];
1037 p.a == other || p.b == other
1038 });
1039 let pair = match found {
1040 Some(pair) => {
1041 let p = &mut self.pairs[pair as usize];
1042 if cand >= p.cand {
1043 return;
1044 }
1045 p.cand = cand;
1046 p.via = via;
1047 if p.a == defect {
1048 p.meet_a = entry;
1049 p.meet_b = other_entry;
1050 } else {
1051 p.meet_a = other_entry;
1052 p.meet_b = entry;
1053 }
1054 pair
1055 }
1056 None => {
1057 let pair = self.pairs.len() as u32;
1058 self.pairs.push(Pair {
1059 a: defect,
1060 b: other,
1061 cand,
1062 meet_a: entry,
1063 meet_b: other_entry,
1064 via,
1065 });
1066 self.incident[defect as usize].push(pair);
1067 self.incident[other as usize].push(pair);
1068 pair
1069 }
1070 };
1071 self.schedule_pair(pair);
1072 }
1073
1074 fn schedule_pair(&mut self, pair: u32) {
1075 let Pair { a, b, cand, .. } = self.pairs[pair as usize];
1076 let (x, y) = (self.top[a as usize], self.top[b as usize]);
1077 if x == y {
1078 return;
1079 }
1080 let rate = self.nodes[x as usize].slope + self.nodes[y as usize].slope;
1081 if rate <= 0 {
1082 return;
1083 }
1084 let slack = cand - self.radius(a) - self.radius(b);
1085 debug_assert!(slack >= 0, "dual infeasible pair: slack {slack}");
1086 let delay = if rate == 2 {
1087 debug_assert!(slack % 2 == 0, "odd slack {slack} between growing nodes");
1088 slack / 2
1089 } else {
1090 slack
1091 };
1092 self.push_event(delay, EVENT_PAIR, pair);
1093 }
1094
1095 fn endpoint_in(&self, pair: u32, node: u32) -> u32 {
1097 let p = &self.pairs[pair as usize];
1098 if self.top[p.a as usize] == node {
1099 p.a
1100 } else {
1101 p.b
1102 }
1103 }
1104
1105 fn child_holding(&self, blossom: u32, defect: u32) -> u32 {
1107 let mut n = defect;
1108 while self.nodes[n as usize].blossom_parent != blossom {
1109 n = self.nodes[n as usize].blossom_parent;
1110 }
1111 n
1112 }
1113
1114 fn other_top(&self, pair: u32, node: u32) -> u32 {
1115 let p = &self.pairs[pair as usize];
1116 let ta = self.top[p.a as usize];
1117 if ta == node {
1118 self.top[p.b as usize]
1119 } else {
1120 ta
1121 }
1122 }
1123
1124 fn parent_node(&self, node: u32) -> u32 {
1125 self.other_top(self.nodes[node as usize].parent, node)
1126 }
1127
1128 fn augment_path(&mut self, node: u32) {
1130 let mut n = node;
1131 while self.nodes[n as usize].parent != NONE {
1132 let t = self.parent_node(n);
1133 let edge = self.nodes[t as usize].parent;
1134 let s = self.parent_node(t);
1135 self.nodes[t as usize].mate = Mate::Pair(edge);
1136 self.nodes[s as usize].mate = Mate::Pair(edge);
1137 n = s;
1138 }
1139 }
1140
1141 fn stuck(&self) -> Stuck {
1142 let detector = (0..self.defects.len())
1143 .filter(|&d| self.nodes[self.top[d] as usize].label == Label::S)
1144 .map(|d| self.defects[d])
1145 .min()
1146 .unwrap_or(0);
1147 Stuck { detector }
1148 }
1149}
1150
1151#[cfg(test)]
1152#[path = "matching_tests.rs"]
1153mod tests;