//! Precedence constraints between tasks. //! //! A precedence constraint says that one task must finish before another //! starts. A set of such constraints forms a directed acyclic graph, and a //! scheduler must respect it: a task cannot be placed until every task that //! must precede it has been placed and has finished. //! //! The graph is stored as adjacency lists. It rejects a cycle when an edge is //! added, because a cycle is not a set of constraints that can be satisfied //! and would otherwise produce an empty topological order that a caller //! might mistake for a scheduling failure rather than a modelling error. //! //! The precedence graph is a separate type from [`Task`](crate::Task) //! because the constraints are a property of the set of tasks rather than of //! any one task, and because a caller may want to plan the same tasks under //! different constraint graphs. //! //! 任务之间的先后约束。 //! //! 一条先后约束说一个任务必须在另一个开始之前完成。一组这样的约束构成有向无环图, //! 而调度器必须遵守它:一个任务在其必须先行的每个任务都被放置并完成之前无法被 //! 放置。 //! //! 该图以邻接表存储。它在添加边时拒绝环,因为环不是一组可满足的约束,且否则会产生 //! 一个空的拓扑序,调用方可能把它误认为调度失败而非建模错误。 //! //! 先后关系图是与 [`Task`](crate::Task) 不同的类型,因为约束是任务集合的性质而非 //! 任一单个任务的性质,也因为调用方可能想在不同约束图下规划同一批任务。 use crate::error::{TaskError, TaskErrorKind}; use crate::task::TaskId; use std::collections::BTreeMap; /// A directed acyclic graph of precedence constraints. /// /// An edge from one task to another means the first must finish before the /// second starts. The order of the tasks themselves is the caller's: the /// graph only records which pairs are related. /// /// 优先约束构成的有向无环图。 /// /// 从一个任务指向另一个任务的边表示前者必须先于后者完成。任务本身的次序 /// 由调用方决定:该图只记录哪些任务对之间存在关系。 #[derive(Debug, Clone, PartialEq, Default)] pub struct PrecedenceGraph { /// The successors of each task, in the order the edges were added. /// /// 各任务的后继,按边添加顺序排列。 successors: BTreeMap>, /// The predecessors of each task, in the order the edges were added. /// /// 各任务的前驱,按边添加顺序排列。 predecessors: BTreeMap>, } impl PrecedenceGraph { /// Creates an empty graph. /// /// 创建一个空图。 pub fn new() -> Self { Self { successors: BTreeMap::new(), predecessors: BTreeMap::new(), } } /// Returns the number of edges. /// /// 返回边的数量。 pub fn edge_count(&self) -> usize { self.successors.values().map(Vec::len).sum() } /// Returns `true` when there are no edges. /// /// 当没有边时返回 `true`。 pub fn is_empty(&self) -> bool { self.successors.values().all(Vec::is_empty) } /// Returns `true` when the constraint from `before` to `after` exists. /// /// 当存在从 `before` 到 `after` 的约束时返回 `true`。 pub fn has_constraint(&self, before: TaskId, after: TaskId) -> bool { self.successors .get(&before.index()) .is_some_and(|successors| successors.contains(&after.index())) } /// Adds a constraint that `before` must finish before `after` starts. /// /// Returns an error when the two identifiers are the same, because a task /// cannot precede itself, or when the constraint would close a cycle. /// /// Adding a constraint that already exists is accepted and has no effect, /// so a caller may assemble a graph from overlapping sources without /// having to deduplicate. /// /// 添加一条约束:`before` 必须在 `after` 开始之前完成。 /// /// 两个标识符相同时返回错误,因为任务不能先于自身;该约束会闭成环时 /// 亦返回错误。 /// /// 添加已存在的约束被接受且不起作用,故调用方可以从多个有重叠的数据源 /// 汇集一个图,无须先去重。 pub fn add(&mut self, before: TaskId, after: TaskId) -> Result<(), TaskError> { if before == after { return Err(TaskError::new(TaskErrorKind::PrecedenceCycle) .with_detail(format!("{before} cannot precede itself"))); } if self.has_constraint(before, after) { return Ok(()); } // A new edge from `before` to `after` closes a cycle exactly when // `before` is already reachable from `after`. That is checked before // the edge is inserted, so a rejected edge leaves the graph // unchanged. // 从 `before` 到 `after` 的新边恰在 `before` 已可自 `after` 到达时 // 闭成环。该检查先于插入,故被拒的边不会改变图。 if self.is_reachable(after, before) { return Err( TaskError::new(TaskErrorKind::PrecedenceCycle).with_detail(format!( "{before} must precede {after}, but {after} already precedes {before}" )), ); } self.successors .entry(before.index()) .or_default() .push(after.index()); self.predecessors .entry(after.index()) .or_default() .push(before.index()); Ok(()) } /// Returns `true` when `to` is reachable from `from` following the /// constraints. /// /// 当沿约束可自 `from` 到达 `to` 时返回 `true`。 pub fn is_reachable(&self, from: TaskId, to: TaskId) -> bool { if from == to { return true; } let mut visited: Vec = Vec::new(); let mut stack = vec![from.index()]; while let Some(node) = stack.pop() { if node == to.index() { return true; } if visited.contains(&node) { continue; } visited.push(node); if let Some(successors) = self.successors.get(&node) { stack.extend(successors.iter().copied()); } } false } /// Returns the tasks that must finish before a task starts. /// /// The result is in the order the constraints were added. It is the /// direct predecessors only, not the transitive closure. /// /// 返回必须在某任务开始前完成的任务。 /// /// 结果按约束添加顺序排列。它只含直接前驱,不含传递闭包。 pub fn predecessors_of(&self, task: TaskId) -> Vec { self.predecessors .get(&task.index()) .map(|list| list.iter().map(|index| TaskId::new(*index)).collect()) .unwrap_or_default() } /// Returns the tasks that must wait for a task to finish. /// /// The result is in the order the constraints were added. It is the /// direct successors only, not the transitive closure. /// /// 返回必须等待某任务完成的任务。 /// /// 结果按约束添加顺序排列。它只含直接后继,不含传递闭包。 pub fn successors_of(&self, task: TaskId) -> Vec { self.successors .get(&task.index()) .map(|list| list.iter().map(|index| TaskId::new(*index)).collect()) .unwrap_or_default() } /// Returns the tasks with no predecessor. /// /// 返回没有前驱的任务。 pub fn roots(&self) -> Vec { // Every task that appears as a successor has a predecessor, so the // roots are the tasks that appear only as a predecessor. // 凡出现为后继的任务都有前驱,故根是那些只作为前驱出现的任务。 let mut roots: Vec = Vec::new(); for task in self.successors.keys() { if !self.predecessors.contains_key(task) && !roots.contains(task) { roots.push(*task); } } roots.sort_unstable(); roots.into_iter().map(TaskId::new).collect() } /// Returns the tasks with no successor. /// /// 返回没有后继的任务。 pub fn leaves(&self) -> Vec { let mut leaves: Vec = Vec::new(); for task in self.predecessors.keys() { if !self.successors.contains_key(task) && !leaves.contains(task) { leaves.push(*task); } } leaves.sort_unstable(); leaves.into_iter().map(TaskId::new).collect() } /// Returns a topological order of the given tasks. /// /// The order respects every constraint: a task appears after all of its /// predecessors. Ties are broken by the position of the task in the /// supplied list, so that a caller can impose its own preference among /// tasks that are otherwise unordered. That is what lets a planner place /// the more valuable tasks first within the freedom the constraints /// allow. /// /// Tasks in the list that have no constraints still appear. /// /// Returns an error when the graph contains a cycle among the given /// tasks. The graph rejects cycles as they are added, so this can only /// happen when a caller builds the graph from one task set and orders /// another, or when the tasks are not all in the graph. /// /// 返回所给任务的拓扑序。 /// /// 该次序满足每一条约束:任务出现在其所有前驱之后。平局按该任务在所给 /// 列表中的位置打破,使调用方可在本来无序的任务间表达自己的偏好。这 /// 正是让规划器在约束允许的自由度内先放置更有价值的任务。 /// /// 列表中无任何约束的任务同样出现。 /// /// 当图在所给任务之间含环时返回错误。该图在添加时就拒绝成环,故这只 /// 可能发生在调用方以一组任务建图却对另一组排序,或任务未全在图中时。 pub fn topological_order(&self, tasks: &[TaskId]) -> Result, TaskError> { // The position of each task in the supplied list, which breaks ties. // 各任务在所给列表中的位置,用于打破平局。 let mut preference: BTreeMap = BTreeMap::new(); for (position, task) in tasks.iter().enumerate() { preference.insert(task.index(), position); } // The number of predecessors of each task that are themselves in the // list. A predecessor outside the list is ignored, because it cannot // be scheduled and so cannot be waited for. // 各任务在列表内的前驱个数。列表外的前驱被忽略,因为它无法被 // 调度,也就无从等待。 let mut pending: BTreeMap = BTreeMap::new(); for task in tasks { let count = self .predecessors_of(*task) .iter() .filter(|predecessor| preference.contains_key(&predecessor.index())) .count(); pending.insert(task.index(), count); } let mut order: Vec = Vec::with_capacity(tasks.len()); let mut placed: Vec = Vec::with_capacity(tasks.len()); while order.len() < tasks.len() { // Among the tasks whose predecessors are all placed, take the // one the caller prefers. // 在前驱均已放置的任务中,取调用方偏好者。 let next = tasks .iter() .map(|task| task.index()) .filter(|index| !placed.contains(index)) .filter(|index| pending.get(index).copied().unwrap_or(0) == 0) .min_by_key(|index| preference.get(index).copied().unwrap_or(usize::MAX)); let Some(next) = next else { return Err(TaskError::new(TaskErrorKind::PrecedenceCycle) .with_detail("no task is free of unplaced predecessors")); }; placed.push(next); order.push(next); for successor in self.successors_of(TaskId::new(next)) { if let Some(count) = pending.get_mut(&successor.index()) { *count = count.saturating_sub(1); } } } Ok(order.into_iter().map(TaskId::new).collect()) } /// Returns the number of tasks in the graph. /// /// 返回图中的任务数。 pub fn task_count(&self) -> usize { let mut all: Vec = self.successors.keys().copied().collect(); for task in self.predecessors.keys() { if !all.contains(task) { all.push(*task); } } all.len() } }