Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: rework tree serialization, accompanying tests #30

Merged
merged 5 commits into from
Aug 9, 2024

Conversation

tangkong
Copy link
Contributor

@tangkong tangkong commented Aug 7, 2024

Description

  • Rework tree config file serialization and in-memory representation to more directly match Behavior Tree Nodes
    • Each dataclass is a BaseItem subclass
    • Each BaseItem has a get_tree method that returns the py_trees.behaviour.Behaviour` specified by the dataclass
    • This keeps logic isolated to each item
  • Reworks tests to match new serialization scheme
    • Keeps test scope focused. If we are testing a serialization round-trip, we don't need to write to file. If we are pulling from a file, don't compare to in-memory representations.
  • One small bugfix, making sure status is set after completion condition is met (this was causing failures for me locally)

Motivation and Context

Closes #18

How Has This Been Tested?

Tests have been adjusted

Where Has This Been Documented?

This PR

Pre-merge checklist

  • Code works interactively
  • Code follows the style guide
  • Code contains descriptive docstrings, including context and API
  • New/changed functions and methods are covered in the test suite where possible
  • Test suite passes locally
  • Test suite passes on GitHub Actions
  • Ran docs/pre-release-notes.sh and created a pre-release documentation page
  • Pre-release docs include context, functional descriptions, and contributors as appropriate

def get_all_subclasses(cls: type) -> Iterator[type]:
"""Recursive implementation of type.__subclasses__"""
for sub_cls in cls.__subclasses__():
yield sub_cls
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beginning to get comfortable with this pythonic yield idea used in recursion, still might need my own toy example to understand it well enough to explain simply

Copy link
Contributor Author

@tangkong tangkong Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: it seems this should actually be hinted as returning a Generator oops

In this case the yield is just pausing calculation and "yielding" the value. Iterators Generators are a way to do make sure we only do calculations when we want the next value.

Using a generator is the same as using a fully defined list or other iterator, we just doesn't know a-priori how long that generator is

Copy link
Member

@ZLLentz ZLLentz Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type hinting documentation presents Iterator[type] and Iterable[type] as fully-allowable sub-ins for Generator[type, None, None] (I think because they behave the same way to the end user)

"Pv": "PERC:COMP",
"Mode": "INC",
"Value": 10
"root": {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wish i could heart react files this looks great!

@as_tagged_union
@dataclass
class ActionItem(BaseItem):
loop_freq: float = 1.0
Copy link
Collaborator

@joshc-slac joshc-slac Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crazy pedantic, I may have even suggested the original name.

Suggested change
loop_freq: float = 1.0
loop_sleep_sec: float = 1.0

Not a huge fan of this either (does not roll off tongue) but makes it very clear to others what it is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critically this is the period, not the frequency 🤦

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True too, but as other things may also take up clock cycles in the action I think indicating it as a sleep could be helpful. Semantics, whatever you pick is good.

beams/tree_config.py Outdated Show resolved Hide resolved
def get_condition_function(self) -> Callable[[], bool]:
op = getattr(operator, self.operator.value)

def cond_func():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder to myself / us in a future PR to potentially nest this in a subclass much the same as we have done with ActionItem that hardcodes this type of Conditional (where we are only cagetting) such that we can have a slightly more generic ConditionNode baseclass

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this should definitely be a PVComparisonConditionItem or something

@tangkong tangkong requested a review from ZLLentz August 8, 2024 00:34
@joshc-slac
Copy link
Collaborator

Linking issue #32, will build off of this awesome work



@dataclass
class BehaviorTreeItem:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if more things should subclass this and not base item so that they have the root object to tick? tell me if I am misunderstanding design

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding of py_trees is that all "trees" should be a py_trees.tree.BehaviourTree. The root node of that BehaviourTree gives the pre/post tick hooks and other logging niceties. https://py-trees.readthedocs.io/en/devel/trees.html

We can individually tick nodes / sub-trees (and we should during testing), but when ingesting a full behavior tree I think this is probably the object we should interact with

@joshc-slac joshc-slac self-requested a review August 8, 2024 20:37
Copy link
Collaborator

@joshc-slac joshc-slac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super potent PR, awesome stuff

Copy link
Member

@ZLLentz ZLLentz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on board, just one seemingly unused and undocumented thing that confused me



def alternative_constructor(func: Func) -> Func:
"""Alternative constructor for a given type."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this function used for? It looks like it essentially registers another function (not stated in the docstrings here), but I don't see any place where this is called in beams. I do see one place where the empty constructors stash is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use it, but we could if we wanted to create an alternative constructor. We don't make use of this but it came as part of the magic spell from wyfo:

@alternative_constructor
def sized_line(
    start: float, stop: float, size: Annotated[float, schema(min=1)]
) -> "Line":
    return Line(start=start, stop=stop, step=(stop - start) / (size - 1))

https://wyfo.github.io/apischema/0.18/examples/subclass_tagged_union/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's used for some un-tagged union parsing, but that's a deeper dive I'd need some time to complete wyfo/apischema#56 (reply in thread)

@tangkong tangkong merged commit 02e53ff into pcdshub:master Aug 9, 2024
6 of 11 checks passed
@tangkong tangkong mentioned this pull request Aug 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MNT: iron out tree serialization, add examples to tests
3 participants