Message schedules
As described in 5. Propose a schedule from the cluster graph, we build a message schedule by calling spanningtrees_clusterlist
on our given cluster graph (output by clustergraph!
).
A schedule of messages can be visualized as a sequence of edge traversals (from sender to recipient) on the cluster graph.
Since the calibration of a cluster graph requires neighbor clusters to reach some state of agreement with each other, it is reasonable to expect that multiple messages may need to be sent back and forth on each edge. Thus, proper message schedules require that each edge is traversed in both directions, infinitely often (until stopping criteria are met).
spanningtrees_clusterlist
satisfies the requirements of a proper message schedule by specifying a finite sequence of edge traversals that together account for all possible messages on the cluster graph. This sequence can then be repeated as needed. Specifically:
- the sequence of edge traversals is returned as a collection of edge sets for different spanning trees of the cluster graph
- each edge set is ordered as a preorder traversal of a spanning tree
Each time calibrate!
is called with a particular tree, it passes messages according to a postorder then preorder traversal of the tree.
Returning to the last few edges of the tree schedule from 5. Propose a schedule from the cluster graph:
julia> DataFrame(parent=sched[1][1], child=sched[1][2])[13:end,:] # last 4 edges of tree 1 in preorder
4×2 DataFrame
Row │ parent child
│ Symbol Symbol
─────┼──────────────────────────────────────────────────────────────────────
1 │ AncientNorthEurasianI1I2NonAfric… EasternNorthAfricanAncientNorthE…
2 │ EasternNorthAfricanAncientNorthE… H1EasternNorthAfricanAncientNort…
3 │ H1EasternNorthAfricanAncientNort… OngeEasternNorthAfrican
4 │ H1EasternNorthAfricanAncientNort… KaritianaH1
According to a postorder, the first message to be sent is from KaritianaH1
to H1EasternNorthAfricanAncientNort…
, followed by OngeEasternNorthAfrican
to H1EasternNorthAfricanAncientNort…
and so on.
An iteration of calibration refers to calibrate!
being called once for each schedule tree in the collection.
Continuing with the code example from A heuristic, we:
- increase the number of iterations of calibration to 100 (the default is 1)
- tell
calibrate!
to return once calibration is detected (auto=true
) - log information on when calibration was detected (
info=true
)
julia> PGBP.init_beliefs_assignfactors!(b, m, tbl_x, df.taxon, net.nodes_changed); # reset to initial beliefs
julia> PGBP.regularizebeliefs_bynodesubtree!(fgb, fg); # regularize by node subtree
julia> PGBP.calibrate!(fgb, sched, 100; auto=true, info=true)
[ Info: calibration reached: iteration 20, schedule tree 1
true
Similarly, during iterative optimization (e.g calibrate_optimize_clustergraph!
), multiple iterations of calibration are run for each set of candidate parameter values (till beliefs are calibrated) to determine the associated factored energy.