1#[cfg(any(test, feature = "legacy_oracle"))]
2use super::DependencyGraph;
3use super::vertex::VertexId;
4#[cfg(any(test, feature = "legacy_oracle"))]
5use formualizer_common::ExcelError;
6#[cfg(any(test, feature = "legacy_oracle"))]
7use rustc_hash::{FxHashMap, FxHashSet};
8
9#[cfg(any(test, feature = "legacy_oracle"))]
10pub struct Scheduler<'a> {
11 graph: &'a DependencyGraph,
12}
13
14#[derive(Debug, Clone)]
15pub struct Layer {
16 pub vertices: Vec<VertexId>,
17 pub(crate) runs: Vec<LayerRun>,
21 pub(crate) sequential: bool,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub(crate) struct LayerRun {
32 pub start: u32,
33 pub len: u32,
34 pub sheet: u16,
35 pub col: u32,
36 pub row0: u32,
37 pub owner: u32,
38}
39
40impl Layer {
41 pub fn new(vertices: Vec<VertexId>) -> Self {
43 Self {
44 vertices,
45 runs: Vec::new(),
46 sequential: false,
47 }
48 }
49
50 pub(crate) fn sub_layer(&self, lo: usize, hi: usize) -> Layer {
52 let first = self
53 .runs
54 .partition_point(|r| (r.start + r.len) as usize <= lo);
55 let runs = self.runs[first..]
56 .iter()
57 .take_while(|r| (r.start as usize) < hi)
58 .map(|r| {
59 let s = (r.start as usize).max(lo);
60 let e = ((r.start + r.len) as usize).min(hi);
61 LayerRun {
62 start: (s - lo) as u32,
63 len: (e - s) as u32,
64 row0: r.row0 + (s - r.start as usize) as u32,
65 ..*r
66 }
67 })
68 .collect();
69 Layer {
70 vertices: self.vertices[lo..hi].to_vec(),
71 runs,
72 sequential: self.sequential,
73 }
74 }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ScheduleUnit {
84 Layer(u32),
85 Cycle(u32),
86}
87
88#[derive(Debug, Clone)]
89pub struct Schedule {
90 pub units: Vec<ScheduleUnit>,
94 pub cycles: Vec<Vec<VertexId>>,
97 pub layers: Vec<Layer>,
99}
100
101impl Schedule {
102 fn from_parts(layers: Vec<Layer>, cycles: Vec<Vec<VertexId>>) -> Self {
103 debug_assert!(
104 cycles.is_empty(),
105 "Schedule::from_parts is the cycle-free fast path"
106 );
107 let units = (0..layers.len() as u32).map(ScheduleUnit::Layer).collect();
108 Schedule {
109 units,
110 cycles,
111 layers,
112 }
113 }
114
115 pub(crate) fn restrict(&self, keep: &super::idset::DenseIdSet) -> Option<(Schedule, usize)> {
123 let mut layers: Vec<Layer> = Vec::new();
124 let mut cycles: Vec<Vec<VertexId>> = Vec::new();
125 let mut units: Vec<ScheduleUnit> = Vec::new();
126 let mut kept = 0usize;
127 for unit in &self.units {
128 match *unit {
129 ScheduleUnit::Layer(i) => {
130 let layer = &self.layers[i as usize];
131 let mut vertices: Vec<VertexId> = Vec::new();
132 let mut runs: Vec<LayerRun> = Vec::new();
133 let mut next_run = 0usize;
134 let mut idx = 0usize;
135 while idx < layer.vertices.len() {
136 if let Some(run) = layer.runs.get(next_run)
138 && run.start as usize == idx
139 {
140 next_run += 1;
141 let end = idx + run.len as usize;
142 let mut open: Option<LayerRun> = None;
143 for (k, &v) in layer.vertices[idx..end].iter().enumerate() {
144 if keep.contains(&v) {
145 let row = run.row0 + k as u32;
146 match open.as_mut() {
147 Some(r) if r.row0 + r.len == row => r.len += 1,
148 _ => {
149 if let Some(r) = open.take().filter(|r| r.len >= 2) {
150 runs.push(r);
151 }
152 open = Some(LayerRun {
153 start: vertices.len() as u32,
154 len: 1,
155 row0: row,
156 ..*run
157 });
158 }
159 }
160 vertices.push(v);
161 } else if let Some(r) = open.take().filter(|r| r.len >= 2) {
162 runs.push(r);
163 }
164 }
165 if let Some(r) = open.take().filter(|r| r.len >= 2) {
166 runs.push(r);
167 }
168 idx = end;
169 continue;
170 }
171 let v = layer.vertices[idx];
172 if keep.contains(&v) {
173 vertices.push(v);
174 }
175 idx += 1;
176 }
177 if !vertices.is_empty() {
178 kept += vertices.len();
179 units.push(ScheduleUnit::Layer(layers.len() as u32));
180 layers.push(Layer {
181 vertices,
182 runs,
183 sequential: layer.sequential,
184 });
185 }
186 }
187 ScheduleUnit::Cycle(i) => {
188 let cycle = &self.cycles[i as usize];
189 let n = cycle.iter().filter(|v| keep.contains(v)).count();
190 if n == 0 {
191 continue;
192 }
193 if n != cycle.len() {
194 return None;
195 }
196 kept += n;
197 units.push(ScheduleUnit::Cycle(cycles.len() as u32));
198 cycles.push(cycle.clone());
199 }
200 }
201 }
202 Some((
203 Schedule {
204 units,
205 cycles,
206 layers,
207 },
208 kept,
209 ))
210 }
211
212 pub fn unit_layer(&self, i: u32) -> &Layer {
214 &self.layers[i as usize]
215 }
216
217 pub fn unit_cycle(&self, i: u32) -> &[VertexId] {
219 &self.cycles[i as usize]
220 }
221}
222
223#[cfg(any(test, feature = "legacy_oracle"))]
224impl<'a> Scheduler<'a> {
225 pub fn new(graph: &'a DependencyGraph) -> Self {
226 Self { graph }
227 }
228
229 pub fn create_schedule(&self, vertices: &[VertexId]) -> Result<Schedule, ExcelError> {
230 let _span = crate::engine::trace::fz_span!(
231 tracing::Level::INFO,
232 "schedule",
233 "schedule.legacy",
234 vertices = vertices.len()
235 );
236 let _scc_span =
238 crate::engine::trace::fz_span!(tracing::Level::INFO, "schedule", "schedule.tarjan");
239 let sccs = self.tarjan_scc(vertices)?;
240 drop(_scc_span);
241
242 let (cycles, acyclic_sccs) = self.separate_cycles(sccs);
244
245 if self.graph.dynamic_topo_enabled() {
247 let subset: Vec<VertexId> = acyclic_sccs.into_iter().flatten().collect();
253 let layers = if subset.is_empty() {
254 Vec::new()
255 } else {
256 self.graph
257 .pk_layers_for(&subset)
258 .unwrap_or(self.build_layers(vec![subset])?)
259 };
260 let mut units = Vec::with_capacity(cycles.len() + layers.len());
261 let mut cycle_order: Vec<usize> = (0..cycles.len()).collect();
262 cycle_order.sort_by_key(|&i| cycles[i].iter().copied().min());
263 for i in cycle_order {
264 units.push(ScheduleUnit::Cycle(i as u32));
265 }
266 units.extend((0..layers.len() as u32).map(ScheduleUnit::Layer));
267 return Ok(Schedule {
268 units,
269 cycles,
270 layers,
271 });
272 }
273
274 if cycles.is_empty() {
275 let layers = self.build_layers(acyclic_sccs)?;
277 return Ok(Schedule::from_parts(layers, cycles));
278 }
279
280 let (units, layers) = self.build_condensation_units(&cycles, acyclic_sccs, None)?;
282 Ok(Schedule {
283 units,
284 cycles,
285 layers,
286 })
287 }
288
289 pub fn create_schedule_with_virtual(
292 &self,
293 vertices: &[VertexId],
294 vdeps: &FxHashMap<VertexId, Vec<VertexId>>,
295 ) -> Result<Schedule, ExcelError> {
296 let _span = crate::engine::trace::fz_span!(
297 tracing::Level::INFO,
298 "schedule",
299 "schedule.virtual",
300 vertices = vertices.len(),
301 vdeps = vdeps.len()
302 );
303 let _scc_span = crate::engine::trace::fz_span!(
305 tracing::Level::INFO,
306 "schedule",
307 "schedule.tarjan_virtual"
308 );
309 let sccs = self.tarjan_scc_with_virtual(vertices, vdeps)?;
310 drop(_scc_span);
311 let (cycles, acyclic_sccs) = self.separate_cycles(sccs);
313 let _layers_span = crate::engine::trace::fz_span!(
315 tracing::Level::INFO,
316 "schedule",
317 "schedule.layers_virtual"
318 );
319 if cycles.is_empty() {
320 let layers = self.build_layers_with_virtual(acyclic_sccs, vdeps)?;
322 return Ok(Schedule::from_parts(layers, cycles));
323 }
324 let (units, layers) = self.build_condensation_units(&cycles, acyclic_sccs, Some(vdeps))?;
327 Ok(Schedule {
328 units,
329 cycles,
330 layers,
331 })
332 }
333
334 pub fn tarjan_scc(&self, vertices: &[VertexId]) -> Result<Vec<Vec<VertexId>>, ExcelError> {
336 self.tarjan_scc_impl(vertices, None)
337 }
338
339 fn tarjan_scc_with_virtual(
341 &self,
342 vertices: &[VertexId],
343 vdeps: &FxHashMap<VertexId, Vec<VertexId>>,
344 ) -> Result<Vec<Vec<VertexId>>, ExcelError> {
345 self.tarjan_scc_impl(vertices, Some(vdeps))
346 }
347
348 fn tarjan_scc_impl(
369 &self,
370 vertices: &[VertexId],
371 vdeps: Option<&FxHashMap<VertexId, Vec<VertexId>>>,
372 ) -> Result<Vec<Vec<VertexId>>, ExcelError> {
373 enum DepList<'g> {
375 Slice(&'g [VertexId]),
376 Owned(Vec<VertexId>),
377 }
378 impl DepList<'_> {
379 #[inline]
380 fn get(&self, i: usize) -> Option<VertexId> {
381 match self {
382 DepList::Slice(s) => s.get(i).copied(),
383 DepList::Owned(v) => v.get(i).copied(),
384 }
385 }
386 }
387
388 let deps_of = |vertex: VertexId| -> DepList<'_> {
389 if let Some(extra) = vdeps.and_then(|m| m.get(&vertex)) {
393 let mut combined: Vec<VertexId> =
394 if let Some(base) = self.graph.dependencies_slice(vertex) {
395 base.to_vec()
396 } else {
397 self.graph.get_dependencies(vertex)
398 };
399 combined.extend(extra.iter().copied());
400 DepList::Owned(combined)
401 } else if let Some(base) = self.graph.dependencies_slice(vertex) {
402 DepList::Slice(base)
403 } else {
404 DepList::Owned(self.graph.get_dependencies(vertex))
405 }
406 };
407
408 const UNVISITED: u32 = u32::MAX;
415 let mut local_of: FxHashMap<VertexId, u32> =
416 FxHashMap::with_capacity_and_hasher(vertices.len(), Default::default());
417 let mut vertex_of_local: Vec<VertexId> = Vec::with_capacity(vertices.len());
418 for &v in vertices {
419 if let std::collections::hash_map::Entry::Vacant(slot) = local_of.entry(v) {
420 slot.insert(vertex_of_local.len() as u32);
421 vertex_of_local.push(v);
422 }
423 }
424 let n = vertex_of_local.len();
425
426 let mut index_counter: u32 = 0;
427 let mut indices: Vec<u32> = vec![UNVISITED; n];
428 let mut lowlinks: Vec<u32> = vec![0; n];
429 let mut on_stack: Vec<bool> = vec![false; n];
430 let mut stack: Vec<u32> = Vec::with_capacity(n);
431 let mut sccs: Vec<Vec<VertexId>> = Vec::new();
432
433 let mut frames: Vec<(u32, DepList<'_>, u32)> = Vec::with_capacity(n);
437
438 for &root_vertex in vertices {
439 let root = local_of[&root_vertex];
440 if indices[root as usize] != UNVISITED {
441 continue;
442 }
443 indices[root as usize] = index_counter;
444 lowlinks[root as usize] = index_counter;
445 index_counter += 1;
446 stack.push(root);
447 on_stack[root as usize] = true;
448 frames.push((root, deps_of(root_vertex), 0));
449
450 while let Some(frame) = frames.last_mut() {
451 let vertex = frame.0 as usize;
452 if let Some(dep_vertex) = frame.1.get(frame.2 as usize) {
453 frame.2 += 1;
454 let Some(&dep) = local_of.get(&dep_vertex) else {
457 continue;
458 };
459 let d = dep as usize;
460 if indices[d] == UNVISITED {
461 indices[d] = index_counter;
463 lowlinks[d] = index_counter;
464 index_counter += 1;
465 stack.push(dep);
466 on_stack[d] = true;
467 frames.push((dep, deps_of(dep_vertex), 0));
468 } else if on_stack[d] {
469 let dep_index = indices[d];
471 if dep_index < lowlinks[vertex] {
472 lowlinks[vertex] = dep_index;
473 }
474 }
475 } else {
476 let vertex_lowlink = lowlinks[vertex];
480 if vertex_lowlink == indices[vertex] {
481 let mut scc = Vec::new();
482 loop {
483 let w = stack.pop().unwrap();
484 on_stack[w as usize] = false;
485 scc.push(vertex_of_local[w as usize]);
486 if w as usize == vertex {
487 break;
488 }
489 }
490 sccs.push(scc);
491 }
492 frames.pop();
493 if let Some(parent) = frames.last() {
494 let p = parent.0 as usize;
495 if vertex_lowlink < lowlinks[p] {
496 lowlinks[p] = vertex_lowlink;
497 }
498 }
499 }
500 }
501 }
502
503 Ok(sccs)
504 }
505
506 #[cfg(test)]
509 pub(crate) fn tarjan_scc_with_virtual_for_tests(
510 &self,
511 vertices: &[VertexId],
512 vdeps: &FxHashMap<VertexId, Vec<VertexId>>,
513 ) -> Result<Vec<Vec<VertexId>>, ExcelError> {
514 self.tarjan_scc_with_virtual(vertices, vdeps)
515 }
516
517 #[cfg(test)]
522 pub(crate) fn tarjan_scc_recursive_reference(
523 &self,
524 vertices: &[VertexId],
525 ) -> Result<Vec<Vec<VertexId>>, ExcelError> {
526 let mut index_counter = 0;
527 let mut stack = Vec::new();
528 let mut indices = FxHashMap::default();
529 let mut lowlinks = FxHashMap::default();
530 let mut on_stack = FxHashSet::default();
531 let mut sccs = Vec::new();
532 let vertex_set: FxHashSet<VertexId> = vertices.iter().copied().collect();
533
534 for &vertex in vertices {
535 if !indices.contains_key(&vertex) {
536 self.tarjan_visit(
537 vertex,
538 &mut index_counter,
539 &mut stack,
540 &mut indices,
541 &mut lowlinks,
542 &mut on_stack,
543 &mut sccs,
544 &vertex_set,
545 )?;
546 }
547 }
548
549 Ok(sccs)
550 }
551
552 #[cfg(test)]
555 pub(crate) fn tarjan_scc_with_virtual_recursive_reference(
556 &self,
557 vertices: &[VertexId],
558 vdeps: &FxHashMap<VertexId, Vec<VertexId>>,
559 ) -> Result<Vec<Vec<VertexId>>, ExcelError> {
560 let mut index_counter = 0;
561 let mut stack = Vec::new();
562 let mut indices = FxHashMap::default();
563 let mut lowlinks = FxHashMap::default();
564 let mut on_stack = FxHashSet::default();
565 let mut sccs = Vec::new();
566 let vertex_set: FxHashSet<VertexId> = vertices.iter().copied().collect();
567
568 for &vertex in vertices {
569 if !indices.contains_key(&vertex) {
570 self.tarjan_visit_with_virtual(
571 vertex,
572 &mut index_counter,
573 &mut stack,
574 &mut indices,
575 &mut lowlinks,
576 &mut on_stack,
577 &mut sccs,
578 &vertex_set,
579 vdeps,
580 )?;
581 }
582 }
583
584 Ok(sccs)
585 }
586
587 #[cfg(test)]
588 #[allow(clippy::too_many_arguments)]
589 fn tarjan_visit(
590 &self,
591 vertex: VertexId,
592 index_counter: &mut usize,
593 stack: &mut Vec<VertexId>,
594 indices: &mut FxHashMap<VertexId, usize>,
595 lowlinks: &mut FxHashMap<VertexId, usize>,
596 on_stack: &mut FxHashSet<VertexId>,
597 sccs: &mut Vec<Vec<VertexId>>,
598 vertex_set: &FxHashSet<VertexId>,
599 ) -> Result<(), ExcelError> {
600 indices.insert(vertex, *index_counter);
602 lowlinks.insert(vertex, *index_counter);
603 *index_counter += 1;
604 stack.push(vertex);
605 on_stack.insert(vertex);
606
607 if let Some(dependencies) = self.graph.dependencies_slice(vertex) {
609 for &dependency in dependencies {
610 if !vertex_set.contains(&dependency) {
612 continue;
613 }
614
615 if !indices.contains_key(&dependency) {
616 self.tarjan_visit(
618 dependency,
619 index_counter,
620 stack,
621 indices,
622 lowlinks,
623 on_stack,
624 sccs,
625 vertex_set,
626 )?;
627 let dep_lowlink = lowlinks[&dependency];
628 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_lowlink));
629 } else if on_stack.contains(&dependency) {
630 let dep_index = indices[&dependency];
632 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_index));
633 }
634 }
635 } else {
636 let dependencies = self.graph.get_dependencies(vertex);
637 for dependency in dependencies {
638 if !vertex_set.contains(&dependency) {
640 continue;
641 }
642
643 if !indices.contains_key(&dependency) {
644 self.tarjan_visit(
646 dependency,
647 index_counter,
648 stack,
649 indices,
650 lowlinks,
651 on_stack,
652 sccs,
653 vertex_set,
654 )?;
655 let dep_lowlink = lowlinks[&dependency];
656 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_lowlink));
657 } else if on_stack.contains(&dependency) {
658 let dep_index = indices[&dependency];
660 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_index));
661 }
662 }
663 }
664
665 if lowlinks[&vertex] == indices[&vertex] {
667 let mut scc = Vec::new();
668 loop {
669 let w = stack.pop().unwrap();
670 on_stack.remove(&w);
671 scc.push(w);
672 if w == vertex {
673 break;
674 }
675 }
676 sccs.push(scc);
677 }
678
679 Ok(())
680 }
681
682 #[cfg(test)]
683 #[allow(clippy::too_many_arguments)]
684 fn tarjan_visit_with_virtual(
685 &self,
686 vertex: VertexId,
687 index_counter: &mut usize,
688 stack: &mut Vec<VertexId>,
689 indices: &mut FxHashMap<VertexId, usize>,
690 lowlinks: &mut FxHashMap<VertexId, usize>,
691 on_stack: &mut FxHashSet<VertexId>,
692 sccs: &mut Vec<Vec<VertexId>>,
693 vertex_set: &FxHashSet<VertexId>,
694 vdeps: &FxHashMap<VertexId, Vec<VertexId>>,
695 ) -> Result<(), ExcelError> {
696 indices.insert(vertex, *index_counter);
698 lowlinks.insert(vertex, *index_counter);
699 *index_counter += 1;
700 stack.push(vertex);
701 on_stack.insert(vertex);
702
703 if let Some(extra) = vdeps.get(&vertex) {
705 let mut dependencies: Vec<VertexId> =
706 if let Some(base) = self.graph.dependencies_slice(vertex) {
707 base.to_vec()
708 } else {
709 self.graph.get_dependencies(vertex)
710 };
711 dependencies.extend(extra.iter().copied());
712
713 for dependency in dependencies {
714 if !vertex_set.contains(&dependency) {
716 continue;
717 }
718
719 if !indices.contains_key(&dependency) {
720 self.tarjan_visit_with_virtual(
722 dependency,
723 index_counter,
724 stack,
725 indices,
726 lowlinks,
727 on_stack,
728 sccs,
729 vertex_set,
730 vdeps,
731 )?;
732 let dep_lowlink = lowlinks[&dependency];
733 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_lowlink));
734 } else if on_stack.contains(&dependency) {
735 let dep_index = indices[&dependency];
737 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_index));
738 }
739 }
740 } else if let Some(dependencies) = self.graph.dependencies_slice(vertex) {
741 for &dependency in dependencies {
742 if !vertex_set.contains(&dependency) {
744 continue;
745 }
746
747 if !indices.contains_key(&dependency) {
748 self.tarjan_visit_with_virtual(
750 dependency,
751 index_counter,
752 stack,
753 indices,
754 lowlinks,
755 on_stack,
756 sccs,
757 vertex_set,
758 vdeps,
759 )?;
760 let dep_lowlink = lowlinks[&dependency];
761 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_lowlink));
762 } else if on_stack.contains(&dependency) {
763 let dep_index = indices[&dependency];
765 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_index));
766 }
767 }
768 } else {
769 let dependencies = self.graph.get_dependencies(vertex);
770 for dependency in dependencies {
771 if !vertex_set.contains(&dependency) {
773 continue;
774 }
775
776 if !indices.contains_key(&dependency) {
777 self.tarjan_visit_with_virtual(
779 dependency,
780 index_counter,
781 stack,
782 indices,
783 lowlinks,
784 on_stack,
785 sccs,
786 vertex_set,
787 vdeps,
788 )?;
789 let dep_lowlink = lowlinks[&dependency];
790 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_lowlink));
791 } else if on_stack.contains(&dependency) {
792 let dep_index = indices[&dependency];
794 lowlinks.insert(vertex, lowlinks[&vertex].min(dep_index));
795 }
796 }
797 }
798
799 if lowlinks[&vertex] == indices[&vertex] {
801 let mut scc = Vec::new();
802 loop {
803 let w = stack.pop().unwrap();
804 on_stack.remove(&w);
805 scc.push(w);
806 if w == vertex {
807 break;
808 }
809 }
810 sccs.push(scc);
811 }
812
813 Ok(())
814 }
815
816 pub(crate) fn separate_cycles(
817 &self,
818 sccs: Vec<Vec<VertexId>>,
819 ) -> (Vec<Vec<VertexId>>, Vec<Vec<VertexId>>) {
820 let mut cycles = Vec::new();
821 let mut acyclic = Vec::new();
822
823 for scc in sccs {
824 if scc.len() > 1 || (scc.len() == 1 && self.has_self_loop(scc[0])) {
825 cycles.push(scc);
826 } else {
827 acyclic.push(scc);
828 }
829 }
830
831 (cycles, acyclic)
832 }
833
834 fn has_self_loop(&self, vertex: VertexId) -> bool {
835 self.graph.has_self_loop(vertex)
836 }
837
838 pub(crate) fn build_layers(
839 &self,
840 acyclic_sccs: Vec<Vec<VertexId>>,
841 ) -> Result<Vec<Layer>, ExcelError> {
842 let vertices: Vec<VertexId> = acyclic_sccs.into_iter().flatten().collect();
843 if vertices.is_empty() {
844 return Ok(Vec::new());
845 }
846 let vertex_set: FxHashSet<VertexId> = vertices.iter().copied().collect();
847
848 let mut in_degrees: FxHashMap<VertexId, usize> = vertices.iter().map(|&v| (v, 0)).collect();
850 for &vertex_id in &vertices {
851 if let Some(dependencies) = self.graph.dependencies_slice(vertex_id) {
852 for &dep_id in dependencies {
853 if vertex_set.contains(&dep_id)
854 && let Some(in_degree) = in_degrees.get_mut(&vertex_id)
855 {
856 *in_degree += 1;
857 }
858 }
859 } else {
860 let dependencies = self.graph.get_dependencies(vertex_id);
861 for dep_id in dependencies {
862 if vertex_set.contains(&dep_id)
863 && let Some(in_degree) = in_degrees.get_mut(&vertex_id)
864 {
865 *in_degree += 1;
866 }
867 }
868 }
869 }
870
871 let mut queue: std::collections::VecDeque<VertexId> = in_degrees
873 .iter()
874 .filter(|&(_, &in_degree)| in_degree == 0)
875 .map(|(&v, _)| v)
876 .collect();
877
878 let mut layers = Vec::new();
879 let mut processed_count = 0;
880
881 while !queue.is_empty() {
882 let mut current_layer_vertices = Vec::new();
883 for _ in 0..queue.len() {
884 let u = queue.pop_front().unwrap();
885 current_layer_vertices.push(u);
886 processed_count += 1;
887
888 if let Some(dependents) = self.graph.dependents_slice(u) {
890 for &v_dep in dependents {
891 if let Some(in_degree) = in_degrees.get_mut(&v_dep) {
892 *in_degree -= 1;
893 if *in_degree == 0 {
894 queue.push_back(v_dep);
895 }
896 }
897 }
898 } else {
899 for v_dep in self.graph.get_dependents(u) {
900 if let Some(in_degree) = in_degrees.get_mut(&v_dep) {
901 *in_degree -= 1;
902 if *in_degree == 0 {
903 queue.push_back(v_dep);
904 }
905 }
906 }
907 }
908 }
909 current_layer_vertices.sort();
911 layers.push(Layer::new(current_layer_vertices));
912 }
913
914 if processed_count != vertices.len() {
915 return Err(
916 ExcelError::new(formualizer_common::ExcelErrorKind::Circ).with_message(
917 "Unexpected cycle detected in acyclic components during layer construction"
918 .to_string(),
919 ),
920 );
921 }
922
923 Ok(layers)
924 }
925
926 fn build_condensation_units(
941 &self,
942 cycles: &[Vec<VertexId>],
943 acyclic_sccs: Vec<Vec<VertexId>>,
944 vdeps: Option<&FxHashMap<VertexId, Vec<VertexId>>>,
945 ) -> Result<(Vec<ScheduleUnit>, Vec<Layer>), ExcelError> {
946 let singletons: Vec<VertexId> = acyclic_sccs.into_iter().flatten().collect();
947 let cycle_node_count = cycles.len();
948 let node_count = cycle_node_count + singletons.len();
949
950 let mut node_of: FxHashMap<VertexId, usize> = FxHashMap::default();
952 for (i, cycle) in cycles.iter().enumerate() {
953 for &v in cycle {
954 node_of.insert(v, i);
955 }
956 }
957 for (j, &v) in singletons.iter().enumerate() {
958 node_of.insert(v, cycle_node_count + j);
959 }
960
961 let mut in_degrees: Vec<usize> = vec![0; node_count];
966 let mut node_dependents: Vec<Vec<usize>> = vec![Vec::new(); node_count];
967 let scan_dep = |from_node: usize,
968 dep: VertexId,
969 in_degrees: &mut Vec<usize>,
970 node_dependents: &mut Vec<Vec<usize>>| {
971 if let Some(&dep_node) = node_of.get(&dep)
972 && dep_node != from_node
973 {
974 in_degrees[from_node] += 1;
975 node_dependents[dep_node].push(from_node);
976 }
977 };
978 for (&v, &n_v) in node_of.iter() {
979 if let Some(deps) = self.graph.dependencies_slice(v) {
980 for &dep in deps {
981 scan_dep(n_v, dep, &mut in_degrees, &mut node_dependents);
982 }
983 } else {
984 for dep in self.graph.get_dependencies(v) {
985 scan_dep(n_v, dep, &mut in_degrees, &mut node_dependents);
986 }
987 }
988 if let Some(extra) = vdeps.and_then(|m| m.get(&v)) {
989 for &dep in extra {
990 scan_dep(n_v, dep, &mut in_degrees, &mut node_dependents);
991 }
992 }
993 }
994
995 let mut current: Vec<usize> = (0..node_count).filter(|&n| in_degrees[n] == 0).collect();
997 let mut units = Vec::new();
998 let mut layers = Vec::new();
999 let mut processed_count = 0usize;
1000 while !current.is_empty() {
1001 let mut wave_cycles: Vec<usize> = current
1002 .iter()
1003 .copied()
1004 .filter(|&n| n < cycle_node_count)
1005 .collect();
1006 wave_cycles.sort_by_key(|&n| cycles[n].iter().copied().min());
1007 for n in wave_cycles {
1008 units.push(ScheduleUnit::Cycle(n as u32));
1009 }
1010
1011 let mut wave_vertices: Vec<VertexId> = current
1012 .iter()
1013 .copied()
1014 .filter(|&n| n >= cycle_node_count)
1015 .map(|n| singletons[n - cycle_node_count])
1016 .collect();
1017 if !wave_vertices.is_empty() {
1018 wave_vertices.sort();
1020 units.push(ScheduleUnit::Layer(layers.len() as u32));
1021 layers.push(Layer::new(wave_vertices));
1022 }
1023
1024 processed_count += current.len();
1025 let mut next = Vec::new();
1026 for &n in ¤t {
1027 for &dependent in &node_dependents[n] {
1028 in_degrees[dependent] -= 1;
1029 if in_degrees[dependent] == 0 {
1030 next.push(dependent);
1031 }
1032 }
1033 }
1034 current = next;
1035 }
1036
1037 if processed_count != node_count {
1038 return Err(
1039 ExcelError::new(formualizer_common::ExcelErrorKind::Circ).with_message(
1040 "Unexpected cycle detected in condensation during unit construction"
1041 .to_string(),
1042 ),
1043 );
1044 }
1045
1046 Ok((units, layers))
1047 }
1048
1049 pub(crate) fn build_layers_with_virtual(
1050 &self,
1051 acyclic_sccs: Vec<Vec<VertexId>>,
1052 vdeps: &FxHashMap<VertexId, Vec<VertexId>>,
1053 ) -> Result<Vec<Layer>, ExcelError> {
1054 use std::collections::VecDeque;
1055 let vertices: Vec<VertexId> = acyclic_sccs.into_iter().flatten().collect();
1056 if vertices.is_empty() {
1057 return Ok(Vec::new());
1058 }
1059 let vertex_set: FxHashSet<VertexId> = vertices.iter().copied().collect();
1060
1061 let mut combined_deps: FxHashMap<VertexId, Vec<VertexId>> = FxHashMap::default();
1063 let mut combined_out: FxHashMap<VertexId, Vec<VertexId>> = FxHashMap::default();
1064 for &v in &vertices {
1065 let mut deps: Vec<VertexId> = Vec::new();
1066 if let Some(base) = self.graph.dependencies_slice(v) {
1067 deps.extend(base.iter().copied().filter(|d| vertex_set.contains(d)));
1068 } else {
1069 deps.extend(
1070 self.graph
1071 .get_dependencies(v)
1072 .into_iter()
1073 .filter(|d| vertex_set.contains(d)),
1074 );
1075 }
1076 if let Some(extra) = vdeps.get(&v) {
1077 deps.extend(extra.iter().copied().filter(|d| vertex_set.contains(d)));
1078 }
1079 deps.sort_unstable();
1080 deps.dedup();
1081 combined_deps.insert(v, deps);
1082 }
1083 for (&v, deps) in combined_deps.iter() {
1085 for &d in deps {
1086 combined_out.entry(d).or_default().push(v);
1087 }
1088 }
1089 let mut in_degrees: FxHashMap<VertexId, usize> = FxHashMap::default();
1091 for &v in &vertices {
1092 let indeg = combined_deps.get(&v).map(|v| v.len()).unwrap_or(0);
1093 in_degrees.insert(v, indeg);
1094 }
1095 let mut queue: VecDeque<VertexId> = in_degrees
1097 .iter()
1098 .filter(|&(_, °)| deg == 0)
1099 .map(|(&v, _)| v)
1100 .collect();
1101
1102 let mut layers = Vec::new();
1103 let mut processed_count = 0;
1104 while !queue.is_empty() {
1105 let mut cur = Vec::new();
1106 for _ in 0..queue.len() {
1107 let u = queue.pop_front().unwrap();
1108 cur.push(u);
1109 processed_count += 1;
1110 if let Some(dependents) = combined_out.get(&u) {
1111 for &w in dependents {
1112 if let Some(ind) = in_degrees.get_mut(&w) {
1113 *ind = ind.saturating_sub(1);
1114 if *ind == 0 {
1115 queue.push_back(w);
1116 }
1117 }
1118 }
1119 }
1120 }
1121 cur.sort_unstable();
1122 layers.push(Layer::new(cur));
1123 }
1124 if processed_count != vertices.len() {
1125 return Err(
1126 ExcelError::new(formualizer_common::ExcelErrorKind::Circ).with_message(
1127 "Unexpected cycle detected in acyclic components during layer construction (virtual)"
1128 .to_string(),
1129 ),
1130 );
1131 }
1132 Ok(layers)
1133 }
1134}