Skip to main content

HEBG: Hierarchial Explainations of Behavior as Graph

Project description

[Fury - PyPi stable version] [PePy - Downloads] [PePy - Downloads per week] [Codacy - Grade] [Codacy - Coverage] [CodeStyle - Black] [Licence - GPLv3]

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.

Installation

pip install hebg

Usage

Build a HEBGraph

Here 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
```

"""

from hebg import HEBGraph, Action, FeatureCondition, Behavior
from hebg.unrolling import unroll_graph

# Add a fundamental action
class Pet(Action):
   def __init__(self) -> None:
      super().__init__(action="Pet")

# Add a condition on the observation
class IsYourHandNearTheCat(FeatureCondition):
   def __init__(self, hand) -> None:
      super().__init__(name="Is hand near the cat ?")
      self.hand = hand
   def __call__(self, observation):
      # Could be a very complex function that returns 1 is the hand is near the cat else 0.
      if observation["cat"] == observation[self.hand]:
            return int(True)  # 1
      return int(False)  # 0

# Add an unexplainable Behavior (without a graph, but a function that can be called).
class MoveYourHandNearTheCat(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 observation
      return Action("Move hand to cat")

# Add a sub-behavior
class PetNearbyCat(Behavior):
   def __init__(self) -> None:
      super().__init__(name="Pet nearby cat")
   def build_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))
      return graph

# Add an other condition on observation
class IsThereACatAround(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" in observation:
            return int(True)  # 1
      return int(False)  # 0

# Add an other unexplainable Behavior (without a graph, but a function that can be called).
class LookForACat(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 observation
      return Action("Move to a cat")

# Finally, add the main Behavior
class PetACat(Behavior):
   def __init__(self) -> None:
      super().__init__(name="Pet a cat")
   def build_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))
      return graph

if __name__ == "__main__":
   pet_a_cat_behavior = PetACat()
   observation = {
      "cat": "sofa",
      "hand": "computer",
   }

   # Call on observation
   action = pet_a_cat_behavior(observation)
   print(action)  # Action("Move hand to cat")

   # Obtain networkx graph
   graph = pet_a_cat_behavior.graph
   print(list(graph.edges(data="index")))

   # Draw graph using matplotlib
   import matplotlib.pyplot as plt
   fig, ax = plt.subplots()
   graph.draw(ax)
   plt.show()
docs/images/PetACatGraph.png

Unrolling HEBGraph

When 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:

from hebg.unrolling import unroll_graph

unrolled_graph = unroll_graph(pet_a_cat_behavior.graph, add_prefix=False)

# Is also a networkx graph
print(list(unrolled_graph.edges(data="index")))

# Draw graph using matplotlib
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
unrolled_graph.draw(ax)
plt.show()
docs/images/PetACatGraphUnrolled.png

Note that unexplainable behaviors (the one without graphs) are kept as is.

Python code generation from graph

Once 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()
with open("pet_a_cat.py", "w") as pyfile:
   pyfile.write(code)

Will generate the code bellow:

from hebg.codegen import GeneratedBehavior

# Require 'Look for a nearby cat' behavior to be given.
# Require 'Move slowly your hand near the cat' behavior to be given.
class PetTheCat(GeneratedBehavior):
   def __call__(self, observation):
      edge_index = self.feature_conditions['Is there a cat around ?'](observation)
      if edge_index == 0:
            return self.known_behaviors['Look for a nearby cat'](observation)
      if edge_index == 1:
            edge_index_1 = self.feature_conditions['Is hand near the cat ?'](observation)
            if edge_index_1 == 0:
               return self.known_behaviors['Move slowly your hand near the cat'](observation)
            if edge_index_1 == 1:
               return self.actions['Action(Pet)'](observation)

Contributing to HEBG

Whenever you encounter a :bug: bug or have :tada: feature request, report this via Github issues.

If you wish to contribute directly, see CONTRIBUTING

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hebg-0.2.4.tar.gz (134.6 kB view details)

Uploaded Source

Built Distribution

hebg-0.2.4-py3-none-any.whl (41.6 kB view details)

Uploaded Python 3

File details

Details for the file hebg-0.2.4.tar.gz.

File metadata

  • Download URL: hebg-0.2.4.tar.gz
  • Upload date:
  • Size: 134.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.1

File hashes

Hashes for hebg-0.2.4.tar.gz
Algorithm Hash digest
SHA256 d756f3f856e768454b8974f57ae8ccc3c96f00197354e79138eb1d560b327e34
MD5 cb362ada132c64d32a7a736090080404
BLAKE2b-256 691ddc979fb1d9210cbbac9aacd4e7e7e90880c700823ac0bda9e06f4cc21be0

See more details on using hashes here.

File details

Details for the file hebg-0.2.4-py3-none-any.whl.

File metadata

  • Download URL: hebg-0.2.4-py3-none-any.whl
  • Upload date:
  • Size: 41.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.1

File hashes

Hashes for hebg-0.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 1097c78d16b000d2ea0bf357dbcc2c6dbce9f781e843089c1a85f4094012d3f2
MD5 f1525af4e9083a4bc8b075bc659d2d64
BLAKE2b-256 b8ee82efcfeec89040fc768d398bda48c16ca9b9c45ed003d6cfb7670feef52d

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page