Hello Plasms!¶
Ok, so you made your first cell in Hello Ecto!. Now lets make a graph, what ecto is especially good at.
c++¶
Lets make two more cells
Download: srcs/Increment.cpp
#include <ecto/ecto.hpp>
using ecto::tendrils;
namespace tutorial
{
struct Increment
{
static void
declare_params(tendrils& params)
{
params.declare<int>("start", "The starting value.", 0);
}
static void
declare_io(const tendrils& params, tendrils& /*in*/, tendrils& out)
{
out.declare<int>("output", "An increasing integer", params.get<int>("start"));
}
int
process(const tendrils& in, const tendrils& out)
{
//the get function of the tendrils returns a mutable reference.
out.get<int>("output") += 1;
return ecto::OK;
}
};
}
ECTO_CELL(tutorial, tutorial::Increment, "Increment", "Outputs a monotonically increasing integer.");
Download: srcs/Add.cpp
#include <ecto/ecto.hpp>
using ecto::tendrils;
namespace tutorial
{
struct Add
{
static void
declare_io(const tendrils& /*params*/, tendrils& in, tendrils& out)
{
in.declare<int>("a", "1st operand.").required(true);
in.declare<int>("b", "2nd operand.").required(true);
out.declare<int>("output", "Result of a + b.");
}
int
process(const tendrils& in, const tendrils& out)
{
out.get<int>("output") = in.get<int>("a") + in.get<int>("b");
return ecto::OK;
}
};
}
ECTO_CELL(tutorial, tutorial::Add, "Add", "Adds two integers together.");
python¶
The python counter part to hello might look like:
Download: srcs/plasms.py
#!/usr/bin/env python
import ecto
from ecto_tutorial.tutorial import Increment, Add
# create three Cell objects
i1 = Increment('Inc 1', start=18)
i2 = Increment('Inc 2', start=20)
add = Add('Add')
# create an empty plasm: a graph
plasm = ecto.Plasm()
# create connections inside that graph: the output named 'output'
# of cell 'i1' is connected to the inut named 'a' of cell 'add'
# and reciprocally with cell 'i2'.
plasm.connect(i1['output'] >> add['a'],
i2['output'] >> add['b'],
)
# create a scheduler that will run that plasm
sched = ecto.Scheduler(plasm)
# execute the plasm
sched.execute(niter=2)
# display the output name 'output' in the outputs of cell 'add'
print add.outputs.output
Lets run it:
No module named PySide.QtCore
42
Parameters¶
Look at the allocation of our cells in the script.
# create three Cell objects
i1 = Increment('Inc 1', start=18)
i2 = Increment('Inc 2', start=20)
add = Add('Add')
The parameters that were declared in:
static void
declare_io(const tendrils& /*params*/, tendrils& in, tendrils& out)
{
in.declare<int>("a", "1st operand.").required(true);
in.declare<int>("b", "2nd operand.").required(true);
out.declare<int>("output", "Result of a + b.");
}
Become the keyword arguments for the cell. The first non keyword argument is the cell’s instance name, which is relevant for pretty looking graphs.
Connections¶
The syntax for connecting ecto cells together in the plasm looks like this.
# create an empty plasm: a graph
plasm = ecto.Plasm()
# create connections inside that graph: the output named 'output'
# of cell 'i1' is connected to the inut named 'a' of cell 'add'
# and reciprocally with cell 'i2'.
plasm.connect(i1['output'] >> add['a'],
i2['output'] >> add['b'],
)
Each connection is made by using the >>
operator, where
the key value in the []
operator refers to an output tendril
on the left hand side, or an input tendril on the right hand side.
See Tendril Connections for more details on how this operator
works.
Graphs¶
The graph for this will look like:
Plasms, graphs, in ecto are Directed Acyclic Graphs , see plasm. The DAG is important for scheduling and determining execution order. It is used to describe the happens before relationships in cells. Because of the DAG construct, implicit feedback loops are not allowed, and can only be achieved through special purpose cells, see Entanglement for more information.
Execution¶
Once a plasm is constructed, it may be used with an ecto scheduler.
# create a scheduler that will run that plasm
sched = ecto.Scheduler(plasm)
# execute the plasm
sched.execute(niter=2)
The ecto.Scheduler
scheduler essentially
does a topological-sort
on the graph, and then executes each cell in order.
This repeats for the specified number of iterations,
or indefinitely if niter
is 0
.
For parallel or threaded execution of plasms, please read the schedulers documentation for a more detailed reference. Note that support for parallel execution is presently restricted to running multiple graphs in parallel. Although one of the project’s initial design goals, it does not at this time introduce parallelism to the internal execution of a graph (this is a very complex thing to manage).
Affecting the Execution of the Graph¶
There are various tricks that can be used to affect/interrupt/halt the direction of execution of the graph.
Various cells can manipulate the graph by grouping or providing some conditional logic that affects the processing of cells that have been combined internally within themselves. See for example, the BlackBox and If cells.
The return value from a cell’s process() function will also instruct the scheduler on how it should continue processing cells in the remainder of the graph. The possible return values are listed below with an explanation for quick reference:
- ecto::OK : directed execution proceeds normally to the next cell in the graph.
- ecto::DO_OVER : repeats processing for that cell.
- ecto::BREAK : abort the current iteration of the graph and return to its starting point.
- ecto::QUIT : completely terminate the execution.
Mutexes/barriers or similar intraprocess mechanisms inside a cell that is present in multiple parallelised graphs will also halt execution of the graph at a particular point.