Builtin Ecto Cells

Ecto contains a few cells that are generally useful for Ecto graph construction.

Convenience cells

The are cells that may be generally useful in your ecto graphs and tend to be weakly typed.

Constant

This cell takes a python parameter that becomes a constant output. As long as this python type is convertable to a c++ type (through python bindings or basic types), it may be connected to cells written in c++.

Brief doc

Constant node always outputs same value.

Parameters

  • value   type: boost::python::api::object    required   default: None

    Value to output

Outputs

  • out   type: boost::python::api::object   

    Any type, constant.

Passthrough

Passthrough is mostly useful from within a BlackBox to connect multiple inputs to one cell, which then becomes a singular input for the BlackBox.

Brief doc

Passes through any type.

Inputs

  • in   type: ecto::tendril::none   

    Any type

Outputs

  • out   type: ecto::tendril::none   

    Any type

TrueEveryN

Brief doc

Will give a true result every n executions.

Parameters

  • count   type: int    not required   default: 0

    Initial value of counter, will be incremented at every call to process.

  • n   type: int    not required   default: 2

    Will be true at every iteration where count%n == 0

Outputs

  • flag   type: bool   

If

The If cell enables you to conditionally execute a single cell based on an input flag.

An example of using the If cell for condition execution of a cell. Notice that cell that is encased in an If is not added to the graph, it is passed to the If cell as a parameter.

#!/usr/bin/env python
import ecto
from ecto import If, TrueEveryN
from ecto.ecto_test import Generate

plasm = ecto.Plasm()
g = Generate("Generator", step=1.0, start=1.0)
if_g = If(cell=g)
truer = TrueEveryN(n=3,count=3)
plasm.connect(truer['flag'] >> if_g['__test__']
              )
plasm.execute(niter=9)
assert g.outputs.out == 3 #should have only called execute 3 times.

The above sample uses the built in cell TrueEveryN

Brief doc

If true, process, else, don’t.

Parameters

  • cell   type: boost::shared_ptr<ecto::cell>    required   no default value

    Cell to conditionally execute. The inputs and outputs of this cell will be replicated to the If cell.

  • input_tendril_name   type: std::string    not required   default: __test__

    Name to use for the conditional input tendril.

Inputs

  • __test__   type: bool   

    The test value. If this is true then cell::process() is called.

Entanglement

To support feedback loops, asynchronous execution of graphs, and conditional execution, ecto supplies a concept of entangled cells, which allow communication of values without breaking the acyclic or synchronous nature of graph execution.

ecto.EntangledPair((Tendril)value[, (str)source_name[, (str)sink_name]]) → tuple :

Constructs a pair of entangled cells. Useful for teleportation of tendrils without constructing edges in a graph.

C++ signature :
boost::python::tuple EntangledPair(boost::shared_ptr<ecto::tendril> [,std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > [,std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >]])

EntangledPair is useful when you would like to feed the output of one cell into the input of another cell, without creating a cycle in the graph. Keep in mind that the first execution of the graph will result in the default value of the whatever the source is connected to being used.

Here is an example of using the EntangledPair for feedback in a graph.

#!/usr/bin/env python
import ecto
from ecto.ecto_test import Generate, Add

g = Generate("Generator", step=1.0, start=1.0)
add = Add()
source, sink = ecto.EntangledPair(value=add.inputs.at('left'))

plasm = ecto.Plasm()
plasm.connect(source[:] >> add['left'],
              g[:] >> add['right'],
              add[:] >> sink[:]
              )

for i in range(0, 5):
    plasm.execute(niter=1)
    print add.outputs.out

The output is:

No module named PySide.QtCore
1.0
3.0
6.0
10.0
15.0

And the graph looks like the following. Notice that there is no cycle:

digraph G {
graph [rankdir=TB, ranksep=1]
edge [labelfontsize=8]
node [shape=plaintext]
0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">  <TR> <TD ROWSPAN="1" COLSPAN="1" BGCOLOR="khaki">EntangledSource</TD>  </TR>  <TR>
<TD PORT="o_out" BGCOLOR="indianred1">out</TD>
</TR> </TABLE>>];
1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"> <TR>
<TD PORT="i_left" BGCOLOR="springgreen">left</TD>
<TD PORT="i_right" BGCOLOR="springgreen">right</TD>
</TR> <TR> <TD ROWSPAN="1" COLSPAN="2" BGCOLOR="khaki">ecto_test::Add</TD>  </TR>  <TR>
<TD PORT="o_out" BGCOLOR="indianred1">out</TD>
</TR> </TABLE>>];
2[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">  <TR> <TD ROWSPAN="3" COLSPAN="1" BGCOLOR="khaki">Generator</TD> <TD PORT="p_start" BGCOLOR="lightblue">start</TD>
 </TR> <TR> <TD PORT="p_step" BGCOLOR="lightblue">step</TD> </TR>
<TR> <TD PORT="p_stop" BGCOLOR="lightblue">stop</TD> </TR>
 <TR>
<TD PORT="o_out" BGCOLOR="indianred1">out</TD>
</TR> </TABLE>>];
3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"> <TR>
<TD PORT="i_in" BGCOLOR="springgreen">in</TD>
</TR> <TR> <TD ROWSPAN="1" COLSPAN="1" BGCOLOR="khaki">EntangledSink</TD>  </TR>   </TABLE>>];
0->1 [headport="i_left" tailport="o_out"];
2->1 [headport="i_right" tailport="o_out"];
1->3 [headport="i_in" tailport="o_out"];
}

Dealer

It may be that you have some iterable in python that you would like to pay out to your graph, one item at a time. Enter the Dealer, think blackjack.

ecto.Dealer(*args, **kwargs)

Emit values of python iterable

Parameters:
  • iterable [boost::python::api::object] REQUIRED

    iterable python object... values to be output

  • tendril [boost::shared_ptr<ecto::tendril>] REQUIRED

    Destination tendril... used to set output type

Outputs:
  • out [ecto::tendril::none]

    Any type

The argument typer should be a tendril, which already has a type associated with it. This is used to prime the dealer’s type erasure, so that boost python objects are converted immediately to this type.

Here is an example of using the Dealer.

#!/usr/bin/env python

import ecto
import ecto.ecto_test as ecto_test

plasm = ecto.Plasm()
printer = ecto_test.Printer()
cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
dealer = ecto.Dealer(tendril=printer.inputs.at('in'), iterable=cards)
plasm.connect(dealer['out'] >> printer['in'])

if __name__ == '__main__':
    Scheduler = ecto.Scheduler
    sched = Scheduler(plasm)
    sched.execute()
    assert dealer.outputs.at('out').type_name == 'double'
    assert dealer.outputs.out == 10

The output is:

No module named PySide.QtCore
***** 1 ***** 0x1a01e60
***** 2 ***** 0x1a01e60
***** 3 ***** 0x1a01e60
***** 4 ***** 0x1a01e60
***** 5 ***** 0x1a01e60
***** 6 ***** 0x1a01e60
***** 7 ***** 0x1a01e60
***** 8 ***** 0x1a01e60
***** 9 ***** 0x1a01e60
***** 10 ***** 0x1a01e60

And the graph looks like the following. Notice that there is no cycle:

digraph G {
graph [rankdir=TB, ranksep=1]
edge [labelfontsize=8]
node [shape=plaintext]
0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">  <TR> <TD ROWSPAN="2" COLSPAN="1" BGCOLOR="khaki">ecto::Dealer</TD> <TD PORT="p_iterable" BGCOLOR="lightblue">iterable</TD>
 </TR> <TR> <TD PORT="p_tendril" BGCOLOR="lightblue">tendril</TD> </TR>
 <TR>
<TD PORT="o_out" BGCOLOR="indianred1">out</TD>
</TR> </TABLE>>];
1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"> <TR>
<TD PORT="i_in" BGCOLOR="springgreen">in</TD>
</TR> <TR> <TD ROWSPAN="1" COLSPAN="1" BGCOLOR="khaki">ecto_test::Printer</TD> <TD PORT="p_print_type" BGCOLOR="lightblue">print_type</TD>
 </TR>   </TABLE>>];
0->1 [headport="i_in" tailport="o_out"];
}