Source code for being.execution
"""Block execution. Given some interconnected blocks find a suitable *order* to
execute them in. Aka. calling the :meth:`being.block.Block.update` methods in an
appropriate order.
Caution:
If the block network graph has cycles it is not possible to resolve a
*proper* execution order. In this case blocks will have to work with out of
date data.
.. digraph:: cycle
:align: center
:alt: Directed graph with cycle
:caption: Directed graph with cycle
:name: Directed graph with cycle
node [shape=box];
bgcolor="#ffffff00"
a -> b -> c;
b -> d;
d -> b;
{rank = same; a; b; c;}
This graph has a cycle between *b* and *d* and the resulting execution order
is *[a, b, c, d]* (*c* and *d* could be swapped depending on the insertion
order).
"""
import collections
from typing import Iterable, List
from being.block import Block, output_neighbors, input_neighbors
from being.graph import Graph, topological_sort
ExecOrder = List[Block]
"""List of topological sorted blocks."""
[docs]def block_network_graph(blocks: Iterable[Block]) -> Graph:
"""Traverse block network and build block network graph.
Args:
blocks: Starting blocks for graph traversal.
Returns:
Block network graph.
"""
vertices = []
edges = set()
queue = collections.deque(blocks)
while queue:
block = queue.popleft()
if block not in vertices:
vertices.append(block)
for successor in output_neighbors(block):
edges.add((block, successor))
queue.append(successor)
for predecessor in input_neighbors(block):
edges.add((predecessor, block))
queue.append(predecessor)
return Graph(vertices, edges)
[docs]def determine_execution_order(blocks: Iterable[Block]) -> ExecOrder:
"""Determine execution order for blocks.
Args:
blocks: Interconnected blocks.
Returns:
Execution order.
"""
graph = block_network_graph(blocks)
return topological_sort(graph)
[docs]def execute(execOrder: ExecOrder):
"""Execute execution order.
Args:
execOrder: Blocks to execute.
"""
for block in execOrder:
block.update()