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

Use analytical solver for BitLengthSet instead of numerical computation #66

Merged
merged 21 commits into from
Apr 7, 2021

Conversation

pavel-kirienko
Copy link
Member

@pavel-kirienko pavel-kirienko commented Apr 5, 2021

Fixes #23

This change request slightly alters the BitLengthSet API by adding handles for the underlying solver but the old entries are kept in place to not break existing applications. The old entries are still susceptible to combinatorial explosion so in order to take advantage of the solver, existing applications (Nunavut) should be updated. The update amounts to ditching all calls to BitLengthSet.__iter__, BitLengthSet.__len__, BitLengthSet.__eq__ because they trigger numerical expansion. Instead, new attributes min, max, fixed_length, and __mod__ should be used as they are evaluated in linear time (also the results are cached).

If PyDSDL is asked to perform a slow numerical expansion, the stack trace of the request is logged to help one hunt down dependencies on the old API.

I am going to update the Nunavut templates soon. While doing that, I am likely to find deficiencies of the new implementation so the version is set to dev for now.

DSDL expressions evaluated for @assert, @print, @sealed, [...] etc always trigger numerical expansion for now so one should be careful with these. It is possible to update the expression evaluation logic to take advantage of the solver where possible but this has not been done yet and it is not on my roadmap because it doesn't seem important. I did, however, put a TODO comment or two.

This is how you can install it for testing locally:

pip install git+https://github.com/UAVCAN/pydsdl@combinatorial-explosion

@pavel-kirienko
Copy link
Member Author

@thirtytwobits I tagged you for review but it is not really necessary to dig into the code. Rather, a high-level response like "yup it makes sense" (or don't) is needed. I linked the new docs in the OP post.

pavel-kirienko added a commit to OpenCyphal/pycyphal that referenced this pull request Apr 6, 2021
@pavel-kirienko pavel-kirienko marked this pull request as draft April 6, 2021 03:21
@pavel-kirienko pavel-kirienko marked this pull request as ready for review April 7, 2021 12:30
pavel-kirienko added a commit to OpenCyphal/pycyphal that referenced this pull request Apr 7, 2021
Copy link
Member

@thirtytwobits thirtytwobits left a comment

Choose a reason for hiding this comment

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

Just some suggestions. Nothing blocking.


# ======================================== DEPRECATED METHODS ========================================

def elementwise_sum_k_multicombinations(self, k: int) -> "BitLengthSet": # pragma: no cover
Copy link
Member

Choose a reason for hiding this comment

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

We should add a @deprecated decorator to allow introspection and linting. For example: https://stackoverflow.com/questions/64593550/lint-usages-of-functions-with-deprecated-decorator

Copy link
Member Author

Choose a reason for hiding this comment

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

idk it seems like too much work for such little benefit. PyTest will warn about deprecations; PyCharm recognizes them as-is:

image


# ======================================== AUXILIARY METHODS ========================================

def __eq__(self, other: typing.Any) -> bool:
Copy link
Member

Choose a reason for hiding this comment

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

should we mark this as deprecated to avoid continued use of this method?

Copy link
Member Author

Choose a reason for hiding this comment

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

I updated its implementation since I wrote the OP post. The new implementation is constant-time but currently, it may yield false positives for sets whose min and max are equal (this is documented). I don't think it deserves deprecation since it is useful, especially in testing.


# ======================================== SLOW NUMERICAL METHODS ========================================

def __iter__(self) -> typing.Iterator[int]:
Copy link
Member

Choose a reason for hiding this comment

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

We should mark this and len as deprecated as well. Let's drive all of the slow expansion out of use over time.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can't do it yet because the expression evaluation logic depends on numerical expansion.

pydsdl/_bit_length_set/_symbolic.py Outdated Show resolved Hide resolved
pydsdl/_data_type_builder.py Show resolved Hide resolved
raise TypeError("Invalid element for nullary set operator: %r" % x)

def modulo(self, divisor: int) -> typing.Set[int]:
return set(map(lambda x: x % divisor, self._value))
Copy link
Member

Choose a reason for hiding this comment

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

nit but if _value is immutable you could cache the set values and just return a copy without redoing the mapping.

Copy link
Member Author

Choose a reason for hiding this comment

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

I extracted the caching concern into MemoizationOperator.

@pavel-kirienko pavel-kirienko merged commit cee519f into master Apr 7, 2021
@pavel-kirienko pavel-kirienko deleted the combinatorial-explosion branch April 7, 2021 21:37
pavel-kirienko added a commit to OpenCyphal/nunavut that referenced this pull request Apr 7, 2021
* Migrate the C templates to use the new BitLengthSet API from OpenCyphal/pydsdl#66
* Remove unnecessary base dependency on pydsdl
* Fix incorrect invocations from the template
* Do not unroll fixed-length arrays
pavel-kirienko added a commit to OpenCyphal/pycyphal that referenced this pull request Apr 10, 2021
* Adopt OpenCyphal/pydsdl#66
* Add complex DSDL type to catch regressions
* Do not unroll (de)serialization of fixed-length arrays
* Update Nunavut
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.

Combinatorial explosion
2 participants