Conversation
Every generated POD after the first installs its predecessor in input slot 0, even when it imports no statements from that POD. Charge this slot in both partitioners, report total input-POD usage in diagnostics, and verify that materialization agrees with the solver.
Track external dependencies by builder input position instead of public-statement hash. Distinct input PODs can expose the same statement tree, so hash-based coalescing can undercount input slots and attach the wrong concrete POD during materialization.
External imports are already represented by assigned Open or synthetic nodes, which consume their statement-table slots. Only cross-POD chain imports create additional statements, so remove the duplicate external-import charge from the heuristic, MILP model, and diagnostics.
Add an external-opening-first topological ordering candidate so POD 0 can use its external input slots before later PODs reserve one for their predecessor. Mark frontend Open operations and synthetic republishes in the symbolic costs, retain the existing candidates, and cover a plan that drops from three PODs to two.
77b1be6 to
25f086f
Compare
ed255
left a comment
There was a problem hiding this comment.
LGTM! I've left some minor comments / questions, please take a look before merging.
| pub struct OperationCost { | ||
| /// True for a statement copied directly from an external input POD. | ||
| #[serde(default)] | ||
| pub is_external_opening: bool, |
There was a problem hiding this comment.
Why is this boolean and the others are usize?
Since this is an operation, the usize fields will be 0 or 1, so they could be bool as well? But then I guess adding is easier if they are usize; yet this new field is bool.
| let n_ext_imports = self.external_imports.len() + self.scratch_new_ext_imports.len(); | ||
| let n_ext_pods = self.external_pods.len() + self.scratch_new_ext_pods.len(); | ||
| if !tree_imports_ok(n_producers, n_ext_imports, n_ext_pods, params) { | ||
| if !tree_imports_ok(self.a == 0, n_producers, n_ext_imports, n_ext_pods, params) { |
There was a problem hiding this comment.
could you rename this a field into something more meaningful?
| } | ||
| match input.dep_edges[statement].as_slice() { | ||
| [AbstractDep::External { pod, .. }] => Some(*pod), | ||
| _ => None, |
There was a problem hiding this comment.
is this case reachable? Because we just saw that is_external_opening = true. If it's unreachable I recommend using unreachable!() to be explicit about it.
There was a problem hiding this comment.
In fact, is input.costs[statement].is_external_opening fully coherent with input.dep_edges[statement].as_slice()? If so, wouldn't this be enough?
n external_opening_pod(input: &InputShape, statement: usize) -> Option<usize> {
match input.dep_edges[statement].as_slice() {
[AbstractDep::External { pod, .. }] => Some(*pod),
_ => None,
}
}
Correct several related issues in how MultiPodBuilder accounts for and schedules external inputs.
Both the heuristic and MILP partitioners now use consistent input-POD and statement accounting. Diagnostics report total input-POD usage, and materialization asserts that its concrete input list agrees with the solver.