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.

Install

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 values, one for states (present and absent).

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 erase 5 by 5, nothing is left. That's not zero — zero is a real number (1(0), one unit of absence). That's void. There is simply nothing there anymore.

PhantomOperator allows void inside arrays and handles it in operations so that everything works and there are no 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." If you can avoid operations involving void, avoid them. They don't produce meaningful results — they just keep things from breaking.

Think of it this way:

  • void + 5 = 5 — Adding nothing to 5 is just 5. Void didn't 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 exists in PhantomOperator as a practical necessity, not as a mathematical participant. The real math happens between present and absent numbers. Void is what's left when the math is done and nothing remains.

Void vs Zero vs Absence

These three are fundamentally different things:

What it is Example Has a state? Has magnitude?
Void Absolute nothingness. No number exists here. Remainder after erasing 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.

Quick Start

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

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

Function Naming

PhantomOperator uses arr_ prefixed function names (arr_add, arr_multiply, etc.) so they don't collide with PhantomTrace's add, multiply, etc. This way you can use both libraries together without conflicts:

from absence_calculator import add, multiply, n          # scalar operations
from phantom_operator 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]

Operations

All five PhantomTrace operations work element-wise on arrays.

Void is handled in every operation so nothing breaks, but remember: void is not a real operand. Operations involving void don't compute anything meaningful — they just propagate nothingness so you don't get errors.

Addition & Subtraction

Same-state elements combine magnitudes. When void appears, it acts as an identity for addition (adding nothing changes nothing), but this is not a real calculation:

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

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

Multiplication & Division

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

When void appears in multiplication, the result is void — you can't scale nothingness. This isn't a mathematical rule; it's just what "nothing" means:

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 dict with remainder, erased, and excess.

When erasure leaves nothing behind, the remainder is void — not zero. There is no number left to speak of. Similarly, when there's no over-erasure, the excess is void — there is no excess to report.

The erasure identity: y erased x erased x(0) = y. Erasing x from y, then erasing the flipped x from the result, gives you back y. The erasure undoes itself.

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

result = arr_erase(a, b)
result['remainder']  # → [4, void, 2]         4 left, nothing left, 2 left
result['erased']     # → [3(0), 5(0), 1(0)]   flipped state
result['excess']     # → [void, void, void]    no over-erasure — void, not zero

Toggle

arr_toggle() flips the state of every element. Void elements stay void — you can't flip the state of something that doesn't exist:

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

Toggle is closely related to erasure. Toggling an element is equivalent to fully erasing it by its own value — the remainder is void (nothing left), and the erased portion carries the same magnitude in the flipped state. So toggling 5 (present) gives 5(0) (absent): same value, opposite state.

PhantomOperator's arr_toggle() does this for every element at once across the entire array using vectorized NumPy operations.

Combine

arr_combine() analyzes the state relationship between two arrays element-wise:

from absence_calculator import n

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]   same state: 2, different state: 1

When void appears, combine returns the state info of the non-void element. But this isn't a real combination — there was nothing on the void side to combine with.

Compare

arr_compare() compares the present-value content between two arrays. Returns the difference as present (more in y) or absent (more in x), with void meaning equal — not "void" in the nothingness sense, but "there is no difference to report":

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

arr_compare(a, b)  # → [2(0), void, 2]   x has 2 more, equal, y has 2 more

Trace

arr_trace() evaluates a function over a range of AbsentNumbers and builds the results directly into an AbsentArray. This is the array version of PhantomTrace's trace().

Same-state ranges iterate by magnitude. Cross-state ranges require an explicit ordering (present and absent are different states of the same magnitude — they aren't ordered against each other).

from absence_calculator import n, trace, present, absent, largest, ordering
from phantom_operator 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)])

The function receives each AbsentNumber in the range and can return any AbsentNumber or Void result:

from absence_calculator import erase

result = arr_trace(lambda x: erase(x, n(2)), n(1), n(5))
# Each element is erased by 2 — results collected into an AbsentArray

Array Features

Shapes & Dimensions

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

Indexing

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 — because there is nothing there:

result = arr_erase(absent_array([5]), absent_array([5]))
result['remainder'][0]  # Void — nothing left

Conversion

from absence_calculator import n

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 phantom_operator import 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]

arr_void_mask() tells you which positions are void — which positions have nothing. This is useful for knowing where real data exists and where there is nothing to work with.

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 phantom_operator import arange, arr_multiply
import time

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

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

Dependencies

  • NumPy — array computation engine
  • PhantomTrace >= 0.8.0 — absence calculus framework

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.2.0.tar.gz (9.5 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.2.0-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for phantomoperator-0.2.0.tar.gz
Algorithm Hash digest
SHA256 400adf357b68fbcd46df534b705c71581012e5b7e841e287b1d176566522a79e
MD5 6d20563997962c99a2f62b1e2b1e3c30
BLAKE2b-256 f946b5a8e25e590f524ed6ff2e5fdd9e6bc551adde3d04a573284e33fa9568ae

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for phantomoperator-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 07a217cc748f66de389b85081d56e9c0ea6e58541b0019bce787915832f32849
MD5 15d4de83864f7476d763480226ca4449
BLAKE2b-256 c57ab5f0e4af08ff7bd95dbc478cde7124890e739375f5d235953b10901ce867

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