Skip to main content

Flat-packed memory-efficient prefix trie

Project description

PackedTrie

A memory-compact, fully dynamic, flat-packed prefix trie with minimal overhead, supporting custom Unicode ranges

Installation

Requires Python 3.10 or newer

Install by running in your terminal

pip install git+https://github.com/VArgenti/PackedTrie.git

Initialization and Methods

trie = PackedTrie(encoding='ascii', size_class='default')

Arguments

  • size_class: Decides the scaling of each node's index ability. Each size_class reserves 1 more byte of memory per node, allowing for an ~256 times larger trie at a cost of ~1 extra byte per node. An overfilled trie will throw an OverflowError.

    • options: "tiny", "default", "large", "massive", "massive+", "massive++"
  • encoding: Decides the unicode range each node supports. The wider the range the more memory is reserved per node

    • options:
      • list: A list of any of the below for disjointed unicode ranges
      • tuple: A tuple of a range, eg (97, 122) for lowercase latin alphabet
      • set[str]: A set of allowed chars, eg {"a", "c"}
      • set[int]: A set of allowed unicode points, eg {97, 99} for "a" and "c"
      • str: A predefined unicode range from the ones below
    • Note that each disjoined range is stored as a tuple within a list

Encoding predefined options

Category langs
General bmp, unicode
ASCII & subsets ascii, printable_ascii, digits, punctuation, alphanumeric
Symbols symbols_math, currency_symbols, arrows, misc_technical
Latin script latin_alphabet, latin_alphabet_lowercase, latin_alphabet_uppercase, latin, latin_extended_a, latin_extended_b
Other scripts greek, cyrillic, hebrew, arabic, devanagari, thai, japanese, korean, hangul_compat, cjk
Special formats emojis_basic, emojis_extended, fullwidth_forms

Methods and their time complexity

Method Average Worst Case
rebuild() O(n*m)
insert(), remove() O(k+m) O(m*k)
with_prefix() O(m+w) O(m*log k + w)
__contains__(), has_prefix() O(log k + m) O(m*log k)
__iter__() O(m) per yielded string
__sizeof__() O(log k)
clear(), is_empty(), __len__(), node_count(), memory_efficiency(), __repr__(), allowed_chars(), help() O(1)

Notation

  • n: number of nodes
  • m: length of the relevant string
  • k: number of unique characters used in the trie
  • w: number of strings yielded

Example usage

trie = PackedTrie(encoding='ascii', size_class='default')
trie.insert("foo")

"foo" in trie           # True
trie.with_prefix("fo")  # ["foo"]

What a trie is

A trie is a way to store strings that allows for fast lookup and efficient prefix matching. Each node in the trie is a character from a string and its children represent possible continuations of that string. For example, a trie with the string "FOO" will look as such;

trie_foo

Here each edge represents a character, and the final "O" node has a flag to indicate it is the end of a string. Here’s a trie with multiple strings;

trie_several

This trie contains the strings "BAR", "FOG", "FOO", "FOOL" and "FOOT"

What makes this trie special

The most common way to make a trie is to have each node be a dict, where each entry representes a character and has a pointer to said character's node. This is an easy and fast way to build a trie but comes with a memory overhead of potentially hundred of bytes per node.

This trie can cram each node into as few as 4 bytes per node while still allowing the trie to store a dictionary of (conservatively) ~3 000 000 real words using only the latin alphabet. Approximately 18 times the amount of words in the Oxford English Dictionary. You can also customise the trie to make each node 10 bytes. At 10 bytes per node (massive++ size), no current data center has enough RAM to overflow the trie's indexable space.

The precise data structure

This trie is flat packed, meaning that no parents have pointers to thir children. Each node in this trie is packaged as; its own character, an end-of-word flag, and an index to its children. Each node is kept in a chunk within an array, where all children are grouped together. Through this we can index the first child and do binary search to find the position of the specific child we want to see. For example, a trie with the strings "A", "B", "C" and "D" will look as such;

child_chunk

The first box in each node is its character, the second the end of word flag and the third with an index to where its children are. Here "B" is a terminal node.

For easier indexing, searching and faster operations, we also have a system to preallocated certain sized chunks. We have 'tiers' of bytearrays which we fit all nodes in. In each bytearray, a chunk of children is given a preallocated space appropiate for 2**tier nodes, starting at tier 0. Through this, at trie containing the strings "ABC", "AC", "B" and "C" will look like;

packed_trie

The 3rd slot in each node has an arrow pointing to the first of its children. Note that this is an index, not a pointer.

What this trie is not

Despite the trie being designed to be compact in memory through flat-packing, no other memory optimisations have been made- that is to say this it not a Patricia trie, Radix trie or a DAWG. It's simply a very compact plain trie.

Help

For usage help, run: PackedTrie.help() It will print available encodings and methods.

TODO

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

packedtrie-0.1.0.tar.gz (12.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

packedtrie-0.1.0-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file packedtrie-0.1.0.tar.gz.

File metadata

  • Download URL: packedtrie-0.1.0.tar.gz
  • Upload date:
  • Size: 12.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for packedtrie-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f770fde5496cf89c855cc489c667aa2ed1e6f0ebb427d58e9b82da0fd10830df
MD5 c74291b6879b9670cc96ec3d9c425c26
BLAKE2b-256 b850109f5ffa06d52e96a637d3e465d3db605db5feb3ad9a4dd0b3b7fb229e7b

See more details on using hashes here.

File details

Details for the file packedtrie-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: packedtrie-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for packedtrie-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 407c226b6c6696f1b2a1a6ca8ed0db8835ab587443c54e76fb408348895c5a78
MD5 0e176b04ba7d2cd6c6517cc32e28d51b
BLAKE2b-256 df026d1d3471b2fa438f2c429173e4c5397d1e342f31f347c70087270d05bc13

See more details on using hashes here.

Supported by

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