Package: combinator
CombinatorThe programming model to integrate AI componentsCombinator is a programming model to build integrative AI solutions. Programs can be generated in the form of graphs such that functions are represented by nodes and dataflow between them by edges. It loosly follows the functional style of programming with lazy evaluation. The features of the programming model can be enumerated as following.Dynamical program generation - Can generate aribitrary subprograms at runtimeModularity - Every part of the program is modularExpressivity - Preloaded with required primitives to build complex logicsAbstraction - Can build arbitrary reusable componentsEase of integration - With a proper UI programs can be easily built by connection nodes with edges.API integration - This packaged model contains a basic version of the environment object to make post calls to remote APIsFor more details please checkthispaper andthisone.Get startedThe programming model is built on Python. You can install the package by running the following commandpipinstallcombinatorThen load the package in Python by running the following commandimportcombinatorascbExamplesHere are few program examples in combinatorAdd two constant numbersfromcombinatorimportcreategraph,createnode,addlink,init_world,runpoperation='+'# this example can be run by setting operation as '-' or '*' or '/' or '^' or '=' or '>'graph=creategraph('TestGraph')# Takes graphname as argumentg1=createnode(graph,'iW',init_world)# 1st argument should be the graph object, 2nd the node short name and 3rd argument should be specific parameters needed to create specific nodes. Except for initworld, constant, sensor and actuator 3rd argument is not needed.g2=createnode(graph,'K',2);g3=createnode(graph,'K',3)g4=createnode(graph,operation)addlink(graph,g1);addlink(graph,g2,g1);# first argument should be the graph object, 2nd the node which needs to be connect to its parents and rest of the arguments should be the parent nodes in sequence.addlink(graph,g3,g1);addlink(graph,g4,g3,g2);output=runp(g4,graph)# The first argument should be the terminal node which needs to be run and 2nd the graph objectprint(output[0])Conjuction of two boolean valuesfromcombinatorimportcreategraph,createnode,addlink,init_world,runpoperation='&'# this example can also be run by setting operation as '|'graph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',True);g3=createnode(graph,'K',False)g4=createnode(graph,operation)addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g3,g1);addlink(graph,g4,g3,g2);output=runp(g4,graph)print(output[0])Conditional executionfromcombinatorimportcreategraph,createnode,addlink,init_world,runpgraph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',2)g3=createnode(graph,'K',3);g4=createnode(graph,'=')g5=createnode(graph,'if')addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g3,g1);addlink(graph,g4,g2,g3);addlink(graph,g5,g4,g1,g2);output=runp(g5,graph)print(output[0])Square all elements of list with fmapfromcombinatorimportcreategraph,createnode,addlink,init_world,runpgraph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',[3,4,5]);g3=createnode(graph,'*')g4=createnode(graph,'fm')addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g3,g1,g1);addlink(graph,g4,g3,g2);output=runp(g4,graph)print(output[0])Zip two listsfromcombinatorimportcreategraph,createnode,addlink,init_world,runpgraph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',[3,4,5]);g3=createnode(graph,'K',[14,12,4]);g4=createnode(graph,'zp')addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g3,g1);addlink(graph,g4,g3,g2);output=runp(g4,graph)print(output[0])Run loopfromcombinatorimportcreategraph,createnode,addlink,init_world,runpgraph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',2);g3=createnode(graph,'+')g4=createnode(graph,'lp')addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g3,g1,g1);addlink(graph,g4,g3,g2);output=runp(g4,graph)print(output[0])Run Recursefromcombinatorimportcreategraph,createnode,addlink,init_world,runpgraph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',7);g3=createnode(graph,'K',1);g4=createnode(graph,'+');g5=createnode(graph,'K',100);g6=createnode(graph,'>');g7=createnode(graph,'lg')g8=createnode(graph,'lg')g9=createnode(graph,'rc')addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g3,g1);addlink(graph,g4,g3,g1);addlink(graph,g5,g1);addlink(graph,g6,g1,g5);addlink(graph,g7,g6);addlink(graph,g8,g4);addlink(graph,g9,g8,g7,g2);output=runp(g9,graph)print(output[0])API POST call with actuator and sensorfromcombinatorimportcreategraph,createnode,addlink,init_world,runpgraph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',{'url':'https://httpbin.org/post','headers':{'Content-Type':'application/json'}})g3=createnode(graph,'K','');g4=createnode(graph,'ac','dict');# the third argument in actuator or sensor node is the input type and output type respectively. If no argument is supplied the type is set as 'any'g5=createnode(graph,'sn','any');g6=createnode(graph,'ac','char');g7=createnode(graph,'sn','any');addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g4,g2);addlink(graph,g5,g4);addlink(graph,g3,g5);addlink(graph,g6,g3);addlink(graph,g7,g6);output=runp(g7,graph)print(output[0])Update the environmentfromcombinatorimportinit_worldinit_world.update_env(<environmentobject>)# check environment/apienv.py to find out how to build an environment object. It can serve as an template to build your own custom environment objectGet runtime errorsfromcombinatorimportcreategraph,createnode,addlink,init_world,runp,combinatorruntimeerrorgraph=creategraph()g1=createnode(graph,'iW',init_world)g2=createnode(graph,'K',2);g3=createnode(graph,'K','3')g4=createnode(graph,'-')print(g1,g2,g2,g4)addlink(graph,g1);addlink(graph,g2,g1);addlink(graph,g3,g1);addlink(graph,g4,g3,g2);try:print(runp(g4,graph))exceptintegratorruntimeerrorase:print(e.error)List of available primitivesFollowing are the list of available primitives in combinator:Full Nameshort nameDescriptioninitWorldiWInitializes the environmentconstantKOutputs a constant value as set during node creationidentityidOutputs the input value unchangedadd+Adds two input numbers. Joins two lists. Updates 2nd key-value pair in the 1st.subtract-Subtracts the number in 2nd input port from the 1stmultiply*Multiplies two numbersdivide/Divides the number in 1st input port with respect to the 2ndexponent^Raises the number in the 1st input port to the power of the 2ndconjunction&Does logical AND operation between two inputsdisjunction|Does logical OR operation between two inputsnegate!Inverts the input boolean valuegreater>Outputs True if 1st input is greater than second else Falseequal=Outputs True if 1st input is equal to second else FalseemptylistordictnlOutputs empty key-value pair if input is 'keyvalue' else empty listheadhdOutputs 1st element of the listtailtlOutputs rest of the list except the 1st elementpoppopOutputs thenth element from the list in the 2nd input port. The valuenshould be provided in the 1st input port. It fetches the value corresponding to the keykif 1st input is a key-value pair. In that case the 2nd input should provide the keyk.appendcnAppends an elementeto the list provided in the 2nd input, where the 1st input provides the elementeaddkeyakAdds a key value pair to the key-value pairs provided in the 1st input. The 2nd input should provide the key and 3rd the value.conditionifExecutes the parent graph connected to 2nd input port if 1st input is True else the parent graph of 3rd input port is executed.lambdagraphlgOutputs the parent graph as subgraph. The iW node in the subgraph is replaced with an identity node. Any other initial nodes in the subgraph is connected to the newly created idenity node.applyapOutputs the parent graph as subgraph. The iW node in the subgraph is replaced with an identity node. Any other initial nodes in the subgraph is connected to the newly created idenity node.fmapfmConverts the subgraph corresponding to parentnode1, a function and thereby applies it to each element of the list supplied in the input port 2. It outputs the new list.zipzpJoins two list element wise. The two list should be provided in two input ports. It outputs a list of lists.aggregatoragAggregates the element of a list by an aggregator function. The aggregator function should be provided in input port 1 and the list in input port 2.looplpConverts the subgraph corresponding to parentnode1, a function and applies itnnumber of times to its output. The initial argument of the function will ben, wherenis supplied as an integer to its input port 2.recursercTakes 2 function and one data value of any type as input. The function supplied to input port 1 is applied recursively on the 3rd input until stopping condition is met. The function supplied to input port 2 is applied on 3rd input to evaluate the stopping condition.sensorsnsends a read request to the environment and outputs the recieved dataactuatoracsends and write request to the environment and writes the input data