STArr
Fast sum tree ops in Cython for NumPy arrays. Inspired by Prioritized Experience Replay.
Installation
pip install starr
Quickstart
Initialize a SumTreeArray, a subclass of numpy.ndarray
>>> from starr import SumTreeArray
>>> sumtree_array = SumTreeArray(4, dtype='float32')
>>> sumtree_array
SumTreeArray([0., 0., 0., 0.], dtype=float32)
Or build one from an existing n-dimensional ndarray
>>> import numpy as np
>>> sumtree_array_2d = SumTreeArray(np.array([[1,2,3],[4,5,6]], dtype='int32'))
>>> sumtree_array_2d
SumTreeArray([[1, 2, 3],
[4, 5, 6]], dtype=int32)
Set values like you normally would
>>> sumtree_array[0] = 1
>>> sumtree_array[1:2] = [2]
>>> sumtree_array[np.array([False,False,True,False])] = 3
>>> sumtree_array[-1] = 4
>>> sumtree_array
SumTreeArray([1., 2., 3., 4.], dtype=float32)
A SumTreeArray maintains an internal sum tree, which can be used for fast sampling and sum ops.
>>> sumtree_array.sumtree()
array([ 0., 10., 3., 7.], dtype=float32)
Sample indices (efficiently), where each element is the unnormalized probability of being sampled
>>> sumtree_array.sample(10)
array([2, 3, 3, 3, 3, 1, 2, 2, 2, 0], dtype=int32)
>>> # probability of being sampled
>>> sumtree_array / sumtree_array.sum()
array([0.1, 0.2, 0.3, 0.4], dtype=float32)
>>> # sampled proportions
>>> (sumtree_array.sample(1000)[None] == np.arange(4)[:,None]).mean(axis=1)
array([0.10057, 0.19919, 0.29983, 0.40041])
You can also sample indices from an n-dimensional SumTreeArray
>>> sumtree_array_2d.sample(4)
(array([1, 1, 0, 0]), array([0, 1, 1, 2]))
Use the array's sum method to use the sumtree to calculate sums (quickly)
>>> sumtree_array.sum()
10.0
Memory
Arithmetic operations return ndarray (to avoid expensive tree initialization)
>>> sumtree_array * 2
array([ 2., 4., 6., 8.], dtype=float32)
This is true for get operations as well
>>> sumtree_array[1:3]
array([2., 3.], dtype=float32)
>>> sumtree_array[:]
array([1., 2., 3., 4.], dtype=float32)
However, in-place operations update SumTreeArray
>>> sumtree_array_in_place_op = SumTreeArray(np.array([2,4,6,8]),dtype='float32')
>>> sumtree_array_in_place_op += 1
>>> sumtree_array_in_place_op
SumTreeArray([3., 5., 7., 9.], dtype=float32)
Performance
See latest benchmarks.
Sampling indices is faster than normal sampling methods in numpy
>>> x = SumTreeArray(np.ones(int(1e6)))
>>> %timeit x.sample(100)
55.2 µs ± 6.17 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> y = np.ones(int(1e6))
>>> %timeit np.random.choice(len(y),size=100,p=y/y.sum())
10.8 ms ± 697 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
For large arrays, sum operations over C-contiguous blocks of memory are faster than ndarray, because of the sum tree:
>>> x = SumTreeArray(np.ones((1000,1000)))
>>> %timeit x.sum()
428 ns ± 10.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> y = np.ones((1000,1000))
>>> %timeit y.sum()
272 µs ± 51.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit x.sum(axis=1)
118 µs ± 2.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit y.sum(axis=1)
276 µs ± 68.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Sum operations over non C-contiguous blocks of memory (e.g. along the first axis of a 2d array) are slower:
>>> %timeit x.sum(axis=0)
367 µs ± 28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit y.sum(axis=0)
303 µs ± 6.97 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Set operations are much slower in SumTreeArray than in ndarray, because each set operation updates the tree, but that's okay when using SumTreeArray for applications that rely heavily on sampling and sum operations, such as prioritzed experience replay! In the example below, updating and sampling with SumTreeArray is 150x faster than with ndarray, even though the update operation alone in ndarray is 26x faster than SumTreeArray!
>>> x = SumTreeArray(np.ones(int(1e6)))
>>> # set + sample
>>> %timeit x[-10:] = 2; x.sample(100)
71.4 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> y = np.ones(int(1e6))
>>> y_sum = y.sum() # let's assume we keep track of this efficiently
>>> # set + sample
>>> %timeit y[-10:] = 2; np.random.choice(len(y),size=100,p=y/y_sum)
10.7 ms ± 752 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Metadata
Release files for starr 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| starr-0.3.2.tar.gz | 14.9 kB | Without provenance | Details for starr-0.3.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| starr-0.3.2-cp36-cp36m-macosx_10_9_x86_64.whl | CPython 3.6 | CPython 3.6 pymalloc | macOS 10.9+ x86-64 | Details for starr-0.3.2-cp36-cp36m-macosx_10_9_x86_64.whl |
Total release size: 243.2 kB
Release files / starr-0.3.2.tar.gz
| Download URL | starr-0.3.2.tar.gz |
|---|---|
| Size | 14.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a0ad82a79b2bb2d25b0954de4c9e5411c75ad8aff41a156ee43f47e742b6647e
|
|
BLAKE2b-256 checksum How to use checksums |
cb626e54153a113d1629eab0a470a372ec785e7e52df85f557dc2c224a5c0c07
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.8.0 pkginfo/1.9.6 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/1.0.0 urllib3/1.26.18 tqdm/4.64.1 importlib-metadata/4.2.0 keyring/23.4.1 rfc3986/1.5.0 colorama/0.4.5 CPython/3.6.7
|
Release files / starr-0.3.2-cp36-cp36m-macosx_10_9_x86_64.whl
| Download URL | starr-0.3.2-cp36-cp36m-macosx_10_9_x86_64.whl |
|---|---|
| Size | 228.3 kB |
| Tags | CPython 3.6 CPython 3.6 pymalloc macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
be5af34b78618da880c956437a2c51595b259c2abe85d5233e3310b6318205a4
|
|
BLAKE2b-256 checksum How to use checksums |
54ac4ff9cb90e43157f7d2f8d5d5095e05c03393503f1f3e730927bb49391923
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.8.0 pkginfo/1.9.6 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/1.0.0 urllib3/1.26.18 tqdm/4.64.1 importlib-metadata/4.2.0 keyring/23.4.1 rfc3986/1.5.0 colorama/0.4.5 CPython/3.6.7
|
Security for starr 0.3.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.