Skip to content

60 add avl tree as an alternative backend data structures for copy efficent persistent data structures#61

Open
kaizhx wants to merge 12 commits into
mainfrom
60-add-avl-tree-as-an-alternative-backend-data-structures-for-copy-efficent-persistent-data-structures
Open

60 add avl tree as an alternative backend data structures for copy efficent persistent data structures#61
kaizhx wants to merge 12 commits into
mainfrom
60-add-avl-tree-as-an-alternative-backend-data-structures-for-copy-efficent-persistent-data-structures

Conversation

@kaizhx

@kaizhx kaizhx commented May 13, 2026

Copy link
Copy Markdown

Summary

Adds an initial path-copying persistent AVL tree map implementation based on the existing PathCopyingPersistentTreeMap which is based on a LLRB tree.

Changes

Changes to the existing tree map implementation focus on replacing LLRB specific logic with their AVL counterparts:

  • Added PathCopyingPersistentAvlTreeMap
  • Replaced color metadata stored in nodes with AVL height metadata.
  • Added AVL balance factor calculation based on subtree heights.
  • Replaced LLRB invariant restoration with AVL rebalancing via rebalance().
  • Implemented trinode restructuring for simplified rebalancing logic via restructure().
    (See https://www.csl.mtu.edu/cs2321/www/newLectures/20_AVL_Tree.html for more information on restructuring algorithm)
  • Updated checkAssertions() to validate AVL height and balance invariants instead of LLRB invariants.
  • Reworked insertion via putAndCopy() to use AVL rebalancing.
  • Reworked deletion via removeAndCopy() to use standard BST deletion followed by AVL rebalancing.
  • Implemented Browns improved deletion algorithm for AVL trees in restructure() for the two child deletion case, choosing the predecessor/successor replacement based on the larger subtree height.
    (See https://arxiv.org/pdf/2406.05162 for Browns improved deletion algorithm)

Reused structures

The implementation intentionally keeps most of the existing persistent tree map structure unchanged where logic is not tied to the LLRB tree implementation and only requires the existence of a binary search tree.

  • Immutable node structure
  • Empty map handling
  • Persistent path-copying style
  • Entry iteration
  • helper functions (min/max node, lookup, bounds)
  • Bounded views support (e.g. subMap, headMap, tailMap)

import org.junit.Test;

@SuppressWarnings("MemberName")
public class PathCopyingPersistentAvlTreeMapTest {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The optimal way of setting up the tests would be to have all tests for PersistentMaps (i.e. tests for maps in general) in a single class and execute it as a parameterized test for all of our map implementations. JUnit 4 provides this feature relatively easily. An example implementation of ours can be found here.

I would suggest to add a new class that handles parameterized testing of PersistentMaps (similar to SolverBasedTest0) and then extend PathCopyingPersistentTreeMapTest with it + tests from this class that are not yet present in PathCopyingPersistentTreeMapTest. (The name PathCopyingPersistentTreeMapTest indicates that it runs all kinds of tree based persistent maps, instead of just RB-Trees anyways)
Test cases that use internal API of your AVL-Tree should remain here (if any).

Tests that test the (currently 2, so parameterizing does not make sense yet) maps against each other should be moved to a dedicated test-class with a proper name.

But there are some other classes that only test on PathCopyingPersistentTreeMap, e.g. CopyOnWriteSortedMapTest, that should also be parameterized.


@After
public void tearDown() {
map = null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is not needed. Since your setup method uses @Before, it is guaranteed that you start with a fresh map for each test. This would also allow you to remove the @Nullable annotation (that you don't want to use anyway. Avoiding null and treating it as an error should be the default nowadays)

Comment on lines +70 to +71
private @Nullable PersistentSortedMap<String, String> llrb;
private @Nullable NavigableMap<String, String> reference;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since you don't use these 2 in your setup, you should delete them and use local variables instead.


try {
((PathCopyingPersistentAvlTreeMap<?, ?>) map).checkAssertions();
} catch (IllegalStateException e) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not just throw/not catch the IllegalStateException?

put(null, key, value);
}

private void put(@Nullable String context, String key, String value) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You could improve the naming of this method and add what it does. Also, you should add some JavaDoc to explain the purpose of this method.

@Var NavigableMap<String, String> subsubmap = submap.subMap("aaa", true, "c", false);
assertThat(subsubmap).containsExactly("b", "b");

subsubmap = submap.subMap("aa", /* fromInclusive= */ true, "bb", /* toInclusive= */ false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Whats the purpose of these comments: fromInclusive etc?

return suite;
}

private @Nullable PersistentSortedMap<String, String> map;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why only test <String, String>?

// a library of useful utilities:
// https://github.com/sosy-lab/java-common-lib
//
// SPDX-FileCopyrightText: 2007-2020 Dirk Beyer <https://www.sosy-lab.org>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2007-2026

* Landis (AVL) tree and bottom-up path copying for copy-efficient persistency.
*
* <p>
* TODO: Finish documentation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

;D

import org.junit.Test;

@SuppressWarnings("MemberName")
public class PathCopyingPersistentAvlTreeMapTest {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Did you test all operations (e.g. rotation cases) of AVL trees?

If not, i would suggest to map out cases for which you can trigger all of them and add them here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Add AVL Tree as an Alternative Backend Data-Structures for Copy-Efficent (Persistent) Data-Structures

2 participants