Skip to content

Fix leq for PatriciaTreeMap when branches have uneven depths - #31

Closed
rootkiller6788 wants to merge 1 commit into
facebook:mainfrom
rootkiller6788:fix-patricia-tree-map-leq-implicit
Closed

Fix leq for PatriciaTreeMap when branches have uneven depths#31
rootkiller6788 wants to merge 1 commit into
facebook:mainfrom
rootkiller6788:fix-patricia-tree-map-leq-implicit

Conversation

@rootkiller6788

Copy link
Copy Markdown
Contributor

Problem

The branch-vs-branch case of Node::is_tree_leq_impl in rust/src/datatype/patricia_tree_impl.rs had its two arms swapped. When the two compared Patricia trees have branch prefixes of different lengths (one prefix is a proper prefix of the other), both the implicit-value condition (is_top() vs is_bottom()) and the recursion direction were mirrored relative to the reference implementation in include/sparta/PatriciaTreeCore.h.

This produces incorrect leq results for both map flavors:

  • PatriciaTreeMapAbstractPartition::leq (implicit value = Bottom)
  • PatriciaTreeMapAbstractEnvironment::leq (implicit value = Top)

Concrete counterexample

With Bottom implicit values, {0, 2}.leq({0, 1, 2, 3}) should be true (the missing bindings in the smaller map are Bottom, which is <= any value), but the swapped arm short-circuited on implicit_value.is_top() and returned false.

Symmetrically, with Top implicit values, {0, 1, 2, 3}.leq({0, 2}) should be true (the larger map's extra bindings are <= Top), but it returned false.

Fix

Align the two arms with the C++ is_tree_leq semantics:

  • s_prefix.begins_with(t_prefix) (s is deeper, s's keys are a subset of t's): require implicit_value.is_bottom() and recurse into the matching child of t.
  • t_prefix.begins_with(s_prefix) (t is deeper, t's keys are a subset of s's): require implicit_value.is_top() and recurse into the matching child of s.

Tests

Added test_ptmap_leq_uneven_branch_depths to rust/tests/abstract_partition_test.rs and test_ptmae_leq_uneven_branch_depths to rust/tests/abstract_environment_test.rs. Both fail before the fix and pass after it, and cover the leq direction in both directions.

The branch-vs-branch case of `Node::is_tree_leq_impl` had its two arms
swapped: when the compared trees have prefixes of different lengths (one
prefix is a proper prefix of the other), the implicit-value condition and
the recursion direction were both mirrored relative to the C++ reference
implementation in `PatriciaTreeCore.h`.

This produced wrong `leq` results for `PatriciaTreeMapAbstractPartition`
(implicit Bottom) and `PatriciaTreeMapAbstractEnvironment` (implicit Top)
whenever one map's key set is a strict subset/superset of the other's. For
example, with Bottom implicit values, `{0, 2} <= {0, 1, 2, 3}` incorrectly
returned false.

Align the two arms with the C++ semantics and add regression tests for
both the partition and environment flavors.
@meta-cla meta-cla Bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Aug 20, 2026
@meta-codesync

meta-codesync Bot commented Aug 20, 2026

Copy link
Copy Markdown

@arthaud has imported this pull request. If you are a Meta employee, you can view this in D116755789.

@arthaud

arthaud commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

LGTM, thanks for the fix

@arthaud
arthaud marked this pull request as ready for review August 20, 2026 08:59
@meta-codesync meta-codesync Bot closed this in a41d883 Aug 20, 2026
@meta-codesync meta-codesync Bot added the Merged label Aug 20, 2026
@meta-codesync

meta-codesync Bot commented Aug 20, 2026

Copy link
Copy Markdown

@arthaud merged this pull request in a41d883.

meta-codesync Bot pushed a commit to facebook/redex that referenced this pull request Aug 20, 2026
Summary:
## Problem

The branch-vs-branch case of `Node::is_tree_leq_impl` in `rust/src/datatype/patricia_tree_impl.rs` had its two arms swapped. When the two compared Patricia trees have branch prefixes of different lengths (one prefix is a proper prefix of the other), both the implicit-value condition (`is_top()` vs `is_bottom()`) and the recursion direction were mirrored relative to the reference implementation in `include/sparta/PatriciaTreeCore.h`.

This produces incorrect `leq` results for both map flavors:
- `PatriciaTreeMapAbstractPartition::leq` (implicit value = Bottom)
- `PatriciaTreeMapAbstractEnvironment::leq` (implicit value = Top)

## Concrete counterexample

With Bottom implicit values, `{0, 2}.leq({0, 1, 2, 3})` should be `true` (the missing bindings in the smaller map are Bottom, which is `<=` any value), but the swapped arm short-circuited on `implicit_value.is_top()` and returned `false`.

Symmetrically, with Top implicit values, `{0, 1, 2, 3}.leq({0, 2})` should be `true` (the larger map's extra bindings are `<=` Top), but it returned `false`.

## Fix

Align the two arms with the C++ `is_tree_leq` semantics:
- `s_prefix.begins_with(t_prefix)` (s is deeper, s's keys are a subset of t's): require `implicit_value.is_bottom()` and recurse into the matching child of `t`.
- `t_prefix.begins_with(s_prefix)` (t is deeper, t's keys are a subset of s's): require `implicit_value.is_top()` and recurse into the matching child of `s`.

## Tests

Added `test_ptmap_leq_uneven_branch_depths` to `rust/tests/abstract_partition_test.rs` and `test_ptmae_leq_uneven_branch_depths` to `rust/tests/abstract_environment_test.rs`. Both fail before the fix and pass after it, and cover the `leq` direction in both directions.

X-link: facebook/SPARTA#31

Reviewed By: arnaudvenet

Differential Revision: D116755789

Pulled By: arthaud

fbshipit-source-id: 7a1e7fe82827f5fab4bd3c03893152e50c284f6b
meta-codesync Bot pushed a commit to facebook/redex that referenced this pull request Aug 20, 2026
Summary:
## Problem

The branch-vs-branch case of `Node::is_tree_leq_impl` in `rust/src/datatype/patricia_tree_impl.rs` had its two arms swapped. When the two compared Patricia trees have branch prefixes of different lengths (one prefix is a proper prefix of the other), both the implicit-value condition (`is_top()` vs `is_bottom()`) and the recursion direction were mirrored relative to the reference implementation in `include/sparta/PatriciaTreeCore.h`.

This produces incorrect `leq` results for both map flavors:
- `PatriciaTreeMapAbstractPartition::leq` (implicit value = Bottom)
- `PatriciaTreeMapAbstractEnvironment::leq` (implicit value = Top)

## Concrete counterexample

With Bottom implicit values, `{0, 2}.leq({0, 1, 2, 3})` should be `true` (the missing bindings in the smaller map are Bottom, which is `<=` any value), but the swapped arm short-circuited on `implicit_value.is_top()` and returned `false`.

Symmetrically, with Top implicit values, `{0, 1, 2, 3}.leq({0, 2})` should be `true` (the larger map's extra bindings are `<=` Top), but it returned `false`.

## Fix

Align the two arms with the C++ `is_tree_leq` semantics:
- `s_prefix.begins_with(t_prefix)` (s is deeper, s's keys are a subset of t's): require `implicit_value.is_bottom()` and recurse into the matching child of `t`.
- `t_prefix.begins_with(s_prefix)` (t is deeper, t's keys are a subset of s's): require `implicit_value.is_top()` and recurse into the matching child of `s`.

## Tests

Added `test_ptmap_leq_uneven_branch_depths` to `rust/tests/abstract_partition_test.rs` and `test_ptmae_leq_uneven_branch_depths` to `rust/tests/abstract_environment_test.rs`. Both fail before the fix and pass after it, and cover the `leq` direction in both directions.

X-link: facebook/SPARTA#31

Reviewed By: arnaudvenet

Differential Revision: D116755789

Pulled By: arthaud

fbshipit-source-id: 7a1e7fe82827f5fab4bd3c03893152e50c284f6b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Do not delete this pull request or issue due to inactivity. Merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants