Metadata-Version: 2.4
Name: PhantomOperator
Version: 0.4.1
Summary: High-performance absent-aware n-dimensional arrays powered by NumPy and PhantomTrace.
Author: PhantomTrace Project
License-Expression: MIT
Project-URL: Homepage, https://github.com/jordanbeitchman-spec/PhantomOperator
Project-URL: Repository, https://github.com/jordanbeitchman-spec/PhantomOperator
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: PhantomTrace>=0.8.0
Dynamic: license-file

# PhantomOperator

High-performance absent-aware n-dimensional arrays powered by [NumPy](https://numpy.org/) and [PhantomTrace](https://pypi.org/project/PhantomTrace/).


## Installation

```bash
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

```python
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:

```python
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:

```python
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

```python
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:

```python
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.

```python
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.

```python
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:

```python
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:

```python
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:

```python
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

```python
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:

```python
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.

```python
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

```python
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

```python
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:

```python
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

```python
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

```python
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:

```python
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

- [NumPy](https://numpy.org/) — array computation engine
- [PhantomTrace](https://pypi.org/project/PhantomTrace/) — absence calculus framework


## License

MIT
