Package: hebg
This package is meant to build programatic hierarchical behaviors as graphs
to compare them to human explanations of behavior.We take the definition of “behavior” as a function from observation to action.InstallationpipinstallhebgUsageBuild a HEBGraphHere is an example to show how could we hierarchicaly build an explanable behavior to pet a cat."""

Here is the hierarchical structure that we would want:

```
PetACat:
   IsThereACatAround ?
   -> Yes:
      PetNearbyCat
   -> No:
      LookForACat

PetNearbyCat:
   IsYourHandNearTheCat ?
   -> Yes:
      Pet
   -> No:
      MoveYourHandNearTheCat
```

"""fromhebgimportHEBGraph,Action,FeatureCondition,Behaviorfromhebg.unrollingimportunroll_graph# Add a fundamental actionclassPet(Action):def__init__(self)->None:super().__init__(action="Pet")# Add a condition on the observationclassIsYourHandNearTheCat(FeatureCondition):def__init__(self,hand)->None:super().__init__(name="Is hand near the cat ?")self.hand=handdef__call__(self,observation):# Could be a very complex function that returns 1 is the hand is near the cat else 0.ifobservation["cat"]==observation[self.hand]:returnint(True)# 1returnint(False)# 0# Add an unexplainable Behavior (without a graph, but a function that can be called).classMoveYourHandNearTheCat(Behavior):def__init__(self)->None:super().__init__(name="Move slowly your hand near the cat")def__call__(self,observation,*args,**kwargs)->Action:# Could be a very complex function that returns actions from any given observationreturnAction("Move hand to cat")# Add a sub-behaviorclassPetNearbyCat(Behavior):def__init__(self)->None:super().__init__(name="Pet nearby cat")defbuild_graph(self)->HEBGraph:graph=HEBGraph(self)is_hand_near_cat=IsYourHandNearTheCat(hand="hand")graph.add_edge(is_hand_near_cat,MoveYourHandNearTheCat(),index=int(False))graph.add_edge(is_hand_near_cat,Pet(),index=int(True))returngraph# Add an other condition on observationclassIsThereACatAround(FeatureCondition):def__init__(self)->None:super().__init__(name="Is there a cat around ?")def__call__(self,observation):# Could be a very complex function that returns 1 is there is a cat around else 0.if"cat"inobservation:returnint(True)# 1returnint(False)# 0# Add an other unexplainable Behavior (without a graph, but a function that can be called).classLookForACat(Behavior):def__init__(self)->None:super().__init__(name="Look for a nearby cat")def__call__(self,observation,*args,**kwargs)->Action:# Could be a very complex function that returns actions from any given observationreturnAction("Move to a cat")# Finally, add the main BehaviorclassPetACat(Behavior):def__init__(self)->None:super().__init__(name="Pet a cat")defbuild_graph(self)->HEBGraph:graph=HEBGraph(self)is_a_cat_around=IsThereACatAround()graph.add_edge(is_a_cat_around,LookForACat(),index=int(False))graph.add_edge(is_a_cat_around,PetNearbyCat(),index=int(True))returngraphif__name__=="__main__":pet_a_cat_behavior=PetACat()observation={"cat":"sofa","hand":"computer",}# Call on observationaction=pet_a_cat_behavior(observation)print(action)# Action("Move hand to cat")# Obtain networkx graphgraph=pet_a_cat_behavior.graphprint(list(graph.edges(data="index")))# Draw graph using matplotlibimportmatplotlib.pyplotaspltfig,ax=plt.subplots()graph.draw(ax)plt.show()Unrolling HEBGraphWhen ploting an HEBGraph of a behavior, only the graph of the behavior itself is shown.
To see the full hierarchical graph (including sub-behaviors), we need to unroll the graph as such:fromhebg.unrollingimportunroll_graphunrolled_graph=unroll_graph(pet_a_cat_behavior.graph,add_prefix=False)# Is also a networkx graphprint(list(unrolled_graph.edges(data="index")))# Draw graph using matplotlibimportmatplotlib.pyplotaspltfig,ax=plt.subplots()unrolled_graph.draw(ax)plt.show()Note that unexplainable behaviors (the one without graphs) are kept as is.Python code generation from graphOnce you have a HEBGraph, you can use it to generate a working python code that
replicates the HEBGraph’s behavior:code=pet_a_cat_behavior.graph.generate_source_code()withopen("pet_a_cat.py","w")aspyfile:pyfile.write(code)Will generate the code bellow:fromhebg.codegenimportGeneratedBehavior# Require 'Look for a nearby cat' behavior to be given.# Require 'Move slowly your hand near the cat' behavior to be given.classPetTheCat(GeneratedBehavior):def__call__(self,observation):edge_index=self.feature_conditions['Is there a cat around ?'](observation)ifedge_index==0:returnself.known_behaviors['Look for a nearby cat'](observation)ifedge_index==1:edge_index_1=self.feature_conditions['Is hand near the cat ?'](observation)ifedge_index_1==0:returnself.known_behaviors['Move slowly your hand near the cat'](observation)ifedge_index_1==1:returnself.actions['Action(Pet)'](observation)Contributing to HEBGWhenever you encounter a :bug:bugor have :tada:feature request,
report this viaGithub issues.If you wish to contribute directly, seeCONTRIBUTING