-
Notifications
You must be signed in to change notification settings - Fork 38
Feedback and Delayed Edge
CSP has two methods of late binding a time series to an edge. The first, csp.feedback
, is used to create connections from downstream nodes to upstream nodes in a graph without introducing a cycle.
The second, csp.DelayedEdge
, is used when a single edge may come from many possible sources and will be bound after its passed as an argument to some other node.
CSP graphs are always directed acyclic graphs (DAGs); however, there are some occasions where you may want to feed back the output of a downstream node into a node at a prior rank.
This is usually the case when a node is making a decision that depends on the result of its previous action.
For example, consider a csp.graph
that simulates population dynamics. We have a system of wolves and elk with W wolves and E elk.
We have a node change_wolf_pop
which simulates introducing or removing wolves from the ecosystem. Then we have a node compute_elk_pop
which computes the new expected elk population based on the number of wolves and other factors.
The output of compute_elk_pop
, being the number of expected elks, will feed back into the change_wolf_pop
node. This means the population dynamics model can be wholly separate from our intervention decision.
For use cases like this, the csp.feedback
construct exists. It allows us to pass the output of a downstream node to one upstream so that a recomputation is triggered on the next engine cycle.
Using csp.feedback
, one can wire a feedback as an input to a node, and effectively bind the actual edge that feeds it later in the graph.
Important
A graph containing one or more csp.feedback
edges is still acyclic. The feedback connection will trigger a recomputation of the upstream node on the next engine cycle, which will be at the same engine time as the current cycle. Internally csp.feedback
creates a pair of input and output adapters that are bound together.
-
csp.feedback(ts_type)
:ts_type
is the type of the timeseries (ie int, str). This returns an instance of a feedback object, which will later be bound to a downstream edge.-
out()
: this method returns the timeseries edge which can be passed as an input to your node -
bind(ts)
: this method is called to bind a downstream edge as the source of the feedback
-
Let us demonstrate the usage of csp.feedback
using our wolves and elk example above. The graph code would look something like this:
import csp
from csp import ts
@csp.node
def change_wolf_pop(elks: ts[int], wolf_only_factors: ts[float]) -> ts[int]:
# External factors could be anything here that affects the wolf population
# but not the elk population directly
# compute the desired wolf population here...
return 0
@csp.node
def compute_elk_pop(wolves: ts[int], elk_only_factors: ts[float]) -> ts[int]:
# Similarly external factors here only directly affect the elk population
# compute the new expected elk population here...
return 0
@csp.graph
def population_dynamics():
# create the feedback first so that we can refer to it later
elk_pop_fb = csp.feedback(int)
# update the wolf population, passing feedback out() which isn't bound yet
wolves = change_wolf_pop(elk_pop_fb.out(), csp.const(0.0))
# get elks output from compute_elk_pop
elks = compute_elk_pop(wolves, csp.const(0.0))
# now bind the elk population to the feedback, finishing the "loop"
elk_pop_fb.bind(elks)
We can visualize the graph using csp.show_graph
. We see that it remains acyclic, but since the FeedbackOutputDef
is bound to the FeedbackInputDef
any output tick will loop back in at the next engine cycle.
The delayed edge is similar to csp.feedback
in the sense that it's a time series which is bound after its declared. Delayed edges must be bound exactly once and will raise an error during graph building if unbound.
Delayed edges can also not be used to create a cycle; if the edge is being bound to a downstream output, csp.feedback
must be used instead. Any cycle will be detected by the CSP engine and raise a runtime error.
Delayed edges are useful when the exact input source needed is not known until graph-time; for example, you may want to subscribe to a list of data feeds which will only be known when you construct the graph.
They are also used by some advanced csp.baselib
utilities like DelayedCollect
and DelayedDemultiplex
which help with input and output data processing.
An example usage of csp.DelayedEdge
is below:
import csp
@csp.graph
def delayed_edge():
delayed = csp.DelayedEdge(csp.ts[int])
three = csp.const(2) + delayed
delayed.bind(csp.const(1))
csp.print('three', three)
Executing this graph will give:
2020-01-01 00:00:00 three:3
This wiki is autogenerated. To made updates, open a PR against the original source file in docs/wiki
.
Get Started (Tutorials)
Concepts
- CSP Node
- CSP Graph
- Historical Buffers
- Execution Modes
- Adapters
- Feedback and Delayed Edge
- Common Mistakes
How-to guides
- Use Statistical Nodes
- Create Dynamic Baskets
- Write Adapters:
- Profile CSP Code
References
- API Reference
- Glossary of Terms
- Examples
Developer Guide