Skip to content

Dispatch the runs of a policy that were never ordered - #37

Merged
HanielUlises merged 7 commits into
rollingfrom
executor/parallel-dispatch
Sep 22, 2026
Merged

HanielUlises merged 7 commits into
rollingfrom
executor/parallel-dispatch

Conversation

@HanielUlises

@HanielUlises HanielUlises commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

A policy fixes an order over its actions. Where two consecutive nodes had no reason to be ordered, the mission waits for nothing, and until now it waited anyway: the tree is a chain of sequences and the second action starts when the first has finished.

parallel_groups finds the runs where that costs something — maximal chains of single continuations whose actions are pairwise independent in two senses:

  • classically, answered by the builder from the domain: neither action's effects touch a predicate the other's requirements or effects mention;
  • epistemically, answered in the pass itself and conservatively: the agents the two actions name must be disjoint, and neither node's knowledge requirements may mention an agent the other names.

Each run renders as one Parallel, with what followed its last member following the group. Members keep their own CheckKnowledge and their own ApplyEpistemicUpdate, so a run that was not as independent as it was taken to be fails a guard rather than running past it, and the executor answers that by replanning. The second test is exact for ontic, private and semi-private actions and not for a public announcement, which reaches agents it does not name; that case is what the guards are left to catch.

Off unless asked for, with epistemic_bt_builder's parallel_dispatch. With it off the rendering is the one this builder produced before the pass existed, which a test pins byte for byte.

The mission that shows it

eplansys_demo's two-site survey: the site survey run twice in different hands, with each scout carrying the instrument its own site reads so that neither half can be done by the other's robot. Six goal conjuncts, four designated worlds, an eleven-node branching policy.

The pass finds three runs, and says so:

dispatching together: (goto_north north), (goto_south south)
dispatching together: (relay_north north relay), (scan_south south)

Measured against Aletheia 0.2.0, north dirty and south clean:

dispatch mission
a node at a time 24.0 s
independent runs together 17.0 s

Seventeen is not nine, which is what the two halves would take if each ran from the start. The scan of the north site is where the policy branches, and nothing below a branch begins before the branch resolves, so the south half waits for a scan it has no stake in. Lifting an independent action across a branch point is what this does not do.

Checked

  • 10 new unit tests over the grouping and the rendering, including the switch-arity regression a group inside a branch caused before the group and its continuation were made one element
  • plansys2_epistemic_executor 157 tests, plansys2_epistemic_bt_builder 37, plansys2_epistemic_planner 173, all passing with the linters
  • both dispatches run end to end on the two-site mission, with the goal verified against the state the mission left behind

The site survey twice over, in different hands. Each scout carries the
instrument its own site reads, so neither half of the mission can be done
by the other's robot, and no action of one half appears in a precondition
of the other.

The point of it is the makespan. Eighteen seconds of declared duration
are spent one action at a time whatever the policy permits, and this is
the mission where that is visible: two halves that were never ordered,
dispatched as though they were.
A policy fixes an order over its actions, and where two consecutive
nodes had no reason to be ordered the mission waits for nothing.
parallel_groups finds those runs: maximal chains of single continuations
whose actions are independent classically, which the builder answers from
the domain, and epistemically, which is answered by requiring that the
agents the two actions name be disjoint and that neither node's knowledge
requirements mention the other's agents.

Each run renders as one Parallel, with what followed its last member
following the group. Members keep their own guards and their own updates,
so a run that was not as independent as it was taken to be fails a guard
rather than running past it, and the executor replans.

Off unless a deployment asks for it, with epistemic_bt_builder's
parallel_dispatch. With it off the tree is the one this builder rendered
before the pass existed, which a test pins.
The header is included by tests that have no domain to talk to. Pulling
the client in with it changed what they compile against, and on rolling
the test that asserts a graph is not null stopped compiling: gtest picks
a printer for the type, and the one it picked there does not exist.

Forward declared instead, with the destructor out of line so that a
shared_ptr to an incomplete type is legal where the builder is destroyed.
Rolling's uncrustify indents the continuation of that return differently
from Humble's, and neither of them is worth arguing with.
Measured against the pinned planner rather than a working copy of it:
the policy 0.2.0 returns puts the two drives next to each other, which
the earlier figure's policy did not, and the mission drops from twenty-
four seconds to seventeen.
@HanielUlises
HanielUlises merged commit 68ed0c4 into rolling Sep 22, 2026
4 checks passed
@HanielUlises
HanielUlises deleted the executor/parallel-dispatch branch September 22, 2026 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant