Skip to main content

High-performance absent-aware n-dimensional arrays powered by NumPy and PhantomTrace.

Project description

PhantomOperator

High-performance absent-aware n-dimensional arrays powered by NumPy and PhantomTrace.

Installation

pip install phantomoperator

This automatically installs NumPy and PhantomTrace as dependencies.

What It Does

PhantomOperator brings NumPy-level performance to PhantomTrace's absence calculus. Instead of looping through Python lists of individual AbsentNumber objects, operations run on compact NumPy arrays — two arrays side by side: one for present values, one for absent values.

Important: Void Is Not a State

Void is not a state. Present is a state. Absent is a state. Void is absolute nothingness — it means there is nothing at that position. No number, no state, no calculation, no meaning. It is the complete absence of anything to talk about.

When you subtract 5 by 5, nothing is left. That is not zero — zero is a real number (1(0), one unit of absence). That is void. Subtraction removes the value and forgets it — there is simply nothing there anymore. Erasure is different: erasing 5 by 5 gives 5(0) (calculable absence). The value is still there, just flipped to the other state. Erasure remembers what it removed.

PhantomOperator allows void inside arrays and handles it in operations so that everything works without errors. But void carrying through an operation does not mean anything is being computed. It means: "there was nothing here, so there is still nothing here."

  • void + 5 = 5 — Adding nothing to 5 is just 5. Void did not contribute anything.
  • void × 5 = void — Multiplying by nothing produces nothing. There was no quantity to scale.
  • void - void = void — Nothing minus nothing is still nothing.

Void vs Zero vs Absence

What it is Example Has a state? Has magnitude?
Void Absolute nothingness. No number exists here. Result of subtracting 5 by 5 No No
Zero (0) One unit of absence. A real number: 1(0) The number zero on a number line Yes (absent) Yes (1)
3(0) Three units of absence. A real quantity in the absent state. Three absent things Yes (absent) Yes (3)

Zero is not void. Zero is a real, meaningful number with a state and a magnitude. Void is nothing at all.

Imports

from phantomoperator import AbsentArray, absent_array, arange, ones
from phantomoperator import arr_add, arr_subtract, arr_multiply, arr_divide
from phantomoperator import arr_erase, arr_join, arr_toggle
from phantomoperator import arr_combine, arr_compare, arr_trace

The phantom_operator module name is also supported for backward compatibility:

from phantom_operator import AbsentArray, arr_add

Function Naming

PhantomOperator uses arr_ prefixed function names so they do not collide with PhantomTrace's scalar operations. This lets you use both libraries together without conflicts:

from phantomtrace import add, multiply, n       # scalar operations
from phantomoperator import arr_add, arr_multiply  # array operations

scalar_result = add(n(5), n(3))                                              # → 8
array_result  = arr_add(absent_array([5, 3]), absent_array([2, 4]))          # → [7, 7]

Quick Start

from phantomoperator import AbsentArray, absent_array, arange, ones

a = absent_array([1, 2, 3, 4, 5])                           # all present
b = absent_array([1, 2, 3, 4, 5], states=[0, 1, 0, 1, 0])  # mixed states

c = arange(1, 10)           # [1, 2, 3, ..., 10] all present
d = arange(1, 10, state=1)  # [1, 2, 3, ..., 10] all absent
e = ones(1000)              # 1000 ones, all present

Operations

All five PhantomTrace operations work element-wise on arrays. Void is handled in every operation so nothing breaks — operations involving void propagate nothingness without raising errors.

Addition & Subtraction

Same-state elements combine magnitudes. Cross-state elements produce an unresolved sum (both components preserved). Void acts as an identity for addition:

from phantomoperator import absent_array, arr_add, arr_subtract

a = absent_array([5, 3, 7], states=[0, 0, 1])
b = absent_array([2, 3, 3], states=[0, 0, 1])

arr_add(a, b)       # → [7, 6, 10(0)]
arr_subtract(a, b)  # → [3, void, 4(0)]   (3 - 3: nothing left = void)

Multiplication & Division

State combination rule: present × present = present, present × absent = absent, absent × absent = present.

from phantomoperator import absent_array, arr_multiply, arr_divide

a = absent_array([5, 4, 6], states=[0, 1, 1])
b = absent_array([3, 3, 2], states=[0, 0, 1])

arr_multiply(a, b)  # → [15, 12(0), 12]   (absent × absent = present)
arr_divide(a, b)    # → [1, 1(0), 3]

Erasure

Erasure flips the state of the erased portion. Returns a single combined AbsentArray where the remainder is stored in the present component and the erased portion in the absent component. Over-erasure debt is stored as erased excess.

Unlike subtraction, erasure does not produce void when values are equal. Erasing 5 by 5 gives 5(0) — the value flips state but is still there.

from phantomoperator import absent_array, arr_erase

a = absent_array([7, 5, 3])
b = absent_array([3, 5, 1])

result = arr_erase(a, b)
# → AbsentArray([4 + 3(0), 5(0), 2 + 0])
# Position 0: 4 remains, 3 was erased (now absent)
# Position 1: fully erased — 5(0)
# Position 2: 2 remains, 1 was erased

# Over-erasure — excess becomes erased debt
a2 = absent_array([3, 10])
b2 = absent_array([5,  4])

result2 = arr_erase(a2, b2)
# → AbsentArray([erased 2 + 3(0), 6 + 4(0)])
# Position 0: 3 flipped (3(0)), 2 extra erasure carried as erased excess
# Position 1: 6 remains, 4 was erased

Join

arr_join() concatenates two AbsentArrays into one, preserving all elements, states, and erased excesses:

from phantomtrace import n, VOID
from phantomoperator import absent_array, arr_join

a = absent_array([n(5)(0), n(7), VOID, n(9)])
b = absent_array([n(6), n(7)(0), n(4)])

arr_join(a, b)
# → AbsentArray([5(0), 7, void, 9, 6, 7(0), 4])

Toggle

arr_toggle() flips the state of every element. Void elements stay void:

from phantomoperator import absent_array, arr_toggle

a = absent_array([1, 2, 3], states=[0, 1, 0])
arr_toggle(a)  # → [1(0), 2, 3(0)]

Toggle is equivalent to fully erasing an element by its own value. Toggling 5 gives 5(0) — same value, opposite state. This is why erasure and subtraction are different: subtraction would give void (forgotten), but erasure gives calculable absence (remembered in the other state).

Combine

arr_combine() analyzes the state relationship between two arrays element-wise. Each position contributes a state indicator (1 for present, 1(0) for absent) and those are added together:

from phantomtrace import n, VOID
from phantomoperator import absent_array, arr_combine

a = absent_array([n(1), n(2)], states=[0, 0])
b = absent_array([n(3), n(4)(0)], states=[0, 1])

arr_combine(a, b)
# → [2, 1 + 1(0)]
# Position 0: both present → 2
# Position 1: present + absent → 1 + 1(0)

# Same state, both absent
arr_combine(absent_array([n(3)(0), n(5)(0)]), absent_array([n(7)(0), n(2)(0)]))
# → [2(0), 2(0)]

# With void — counts only the non-void side
arr_combine(absent_array([n(5), n(4)(0)]), absent_array([VOID, VOID]))
# → [1, 1(0)]

Compare

arr_compare() compares how magnitude is distributed between present and absent states. It only works on two arrays that have the same total magnitude at each position. Mismatched magnitudes or void vs number positions raise an error.

When magnitudes match, compare returns:

  • The difference as present when y has more present content
  • The difference as absent when x has more present content
  • Void when the present content is equal
from phantomoperator import absent_array, arr_compare

a = absent_array([5, 3, 7], states=[0, 1, 0])
b = absent_array([5, 3, 7], states=[1, 0, 0])

arr_compare(a, b)
# → [5(0), 3, void]
# Position 0: same magnitude, x present → y absent → x has more present → 5(0)
# Position 1: same magnitude, x absent → y present → y has more present → 3
# Position 2: both present with same value → no difference → void

Mismatched magnitudes are rejected:

arr_compare(absent_array([5]), absent_array([3]))
# → ValueError: total magnitudes differ (5 vs 3)

Trace

arr_trace() evaluates a function over a range of AbsentNumbers and builds the results directly into an AbsentArray. Same-state ranges iterate by magnitude. Cross-state ranges require an explicit ordering.

from phantomtrace import n, present, absent, largest, ordering
from phantomoperator import arr_trace

result = arr_trace(lambda x: x, n(1), n(5))
# → AbsentArray([1, 2, 3, 4, 5])

result = arr_trace(lambda x: x, n(3)(0), n(1)(0))
# → AbsentArray([3(0), 2(0), 1(0)])

order = ordering(largest(present()), largest(absent()))
result = arr_trace(lambda x: x, n(3), n(3)(0), order=order)
# → AbsentArray([3, 2, 1, 1(0), 2(0), 3(0)])

Array Features

Shapes & Dimensions

from phantomoperator import absent_array

a = absent_array([[1, 2, 3], [4, 5, 6]])
a.shape  # (2, 3)
a.ndim   # 2
a.size   # 6

Indexing

from phantomoperator import absent_array

a = absent_array([10, 20, 30, 40, 50])
a[0]    # AbsentNumber: 10
a[1:3]  # AbsentArray([20, 30])

Indexing a void position returns PhantomTrace's Void object:

from phantomoperator import absent_array, arr_subtract

result = arr_subtract(absent_array([5]), absent_array([5]))
result[0]  # Void — subtraction forgot the value entirely

Conversion

from phantomtrace import n
from phantomoperator import absent_array

a = absent_array([n(5), n(3)(0), n(7)])
a.to_list()  # → [AbsentNumber(5), AbsentNumber(3, 1), AbsentNumber(7)]

Void positions in to_list() return PhantomTrace's Void object.

Querying State

from phantomtrace import n, VOID
from phantomoperator import absent_array, arr_count_present, arr_present_mask, arr_absent_mask, arr_void_mask

a = absent_array([n(1), n(2)(0), VOID, n(4), n(5)(0)])

arr_count_present(a)  # 2
arr_present_mask(a)   # [True, False, False, True, False]
arr_absent_mask(a)    # [False, True, False, False, True]
arr_void_mask(a)      # [False, False, True, False, False]

Performance

PhantomOperator is built on NumPy's C arrays, so operations on large arrays are orders of magnitude faster than looping through individual AbsentNumbers:

from phantomoperator import arange, arr_multiply
import time

a = arange(1, 100000)
b = arange(1, 100000)

start = time.time()
result = arr_multiply(a, b)
elapsed = time.time() - start
# Typically < 2ms vs seconds with plain Python lists

Dependencies

License

MIT

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

phantomoperator-0.4.1.tar.gz (11.7 kB view details)

Uploaded Source

Built Distribution

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

phantomoperator-0.4.1-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

File details

Details for the file phantomoperator-0.4.1.tar.gz.

File metadata

  • Download URL: phantomoperator-0.4.1.tar.gz
  • Upload date:
  • Size: 11.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for phantomoperator-0.4.1.tar.gz
Algorithm Hash digest
SHA256 000d432bab04a398108cbc5504a90154806428ffb4c3f521fc23b82e36de14f8
MD5 20ec35cb355649764bd780fc6f3aa753
BLAKE2b-256 0ce6855444651aabfdc3d8d869a683991cc074bb462f8bfe7f28282db80f9e70

See more details on using hashes here.

File details

Details for the file phantomoperator-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for phantomoperator-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0db340f2957cf15f7dac61d0ed3f460cd7da849b5db15057d8d4245a2ca758a4
MD5 8ebf655bdfeede304b91cacb83741bcb
BLAKE2b-256 680e766e5469b4a15fbf680d1e9fbbdbcfc633e348df4b6ef3d7a8f7bb49f2ff

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