EvaluatedApplications.Phasor
3.6.0
dotnet add package EvaluatedApplications.Phasor --version 3.6.0
NuGet\Install-Package EvaluatedApplications.Phasor -Version 3.6.0
<PackageReference Include="EvaluatedApplications.Phasor" Version="3.6.0" />
<PackageVersion Include="EvaluatedApplications.Phasor" Version="3.6.0" />
<PackageReference Include="EvaluatedApplications.Phasor" />
paket add EvaluatedApplications.Phasor --version 3.6.0
#r "nuget: EvaluatedApplications.Phasor, 3.6.0"
#:package EvaluatedApplications.Phasor@3.6.0
#addin nuget:?package=EvaluatedApplications.Phasor&version=3.6.0
#tool nuget:?package=EvaluatedApplications.Phasor&version=3.6.0
Phasor: One Exact Hologram, Many Views
Phasor's main object is the Pleroma: one exact hologram that answers many kinds of question about the same multiset of items. An item is a key plus, optionally, an m-bit flag word, an arrangement of up to 5 things and a tick on a clock. One fixed array of finite-field harmonics gives exact counts per key and whole items (refusing past its key capacity instead of answering wrongly), and exact counts per flag word, Hamming near-counts and nearest words, counts per arrangement, counts per tick and opt-in joint counts over flags, order and time, all of these exact within the field's range. The whole store relabels its flags and arrangements, shifts its keys, and ages or diffuses over time in place, with every view still exact, and a weighted superposition of flag flips, arrangement relabellings and time shifts applies to the whole store in one pass whose cost does not depend on how many of them it holds. Two stores bind in one pass (keys add, flag words XOR, arrangements compose), and one query reads the store three ways at once: exact counts, shares that sum to 1, and the parity of the count. Two companion types read loops and charges: RelationFace gives the holonomy of any loop of stored relations in one linear read, and ChargeLattice reads integer charges from the carries of a lattice's loop sums.
CycleHologram is the single-face special case (keys only: exact multiset storage and replica reconciliation), kept for compatibility. Beneath both sits the continuous phasor codec, documented further down: it encodes numbers and symbols as unit-complex faces and composes them with bind, unbind, bundle and correlate.
Measured against Plate's Holographic Reduced Representations (HRR). At equal storage (about 2,000 to 2,600 bits; measured at about 2,064 and 2,580 bits), the exact key view recovered 64 items in 200 of 200 trials. HRR with its standard decoder or a stronger one held at least 95 percent only up to 4 items. Past capacity (k = 65 to 96), the exact key view refused 12,800 of 12,800 trials with no wrong answer; every HRR baseline answered wrongly every time. These are the figures for these budgets only. The benchmark is not part of the shipped package.
Quick Start: One Store, Many Views (Pleroma)
dotnet add package EvaluatedApplications.Phasor
Minimal Example: Items With Flags and Time
using Phasor;
var store = new Pleroma(new HologramShape
{
KeyCapacity = 16, // whole items come back for up to 16 distinct keys
FlagBits = 8, // an 8-bit flag word per item
TimePeriod = 63, // a 63-tick clock (on the default field it must divide 2^61 - 2)
JointFaces = HologramFaces.Flags | HologramFaces.Time, // also store flags x time joint counts (256 x 63 cells)
});
store.Add(new HologramItem(Key: 1001, Flags: 0b0000_0101, Time: 0), 3);
store.Add(new HologramItem(Key: 2002, Flags: 0b1000_0001, Time: 5), 2);
if (store.TryItems(out var items)) // refuses past 16 keys instead of answering wrongly
foreach (var (item, count) in items)
System.Console.WriteLine($"{item.Key}: flags {item.Flags}, time {item.Time}, count {count}");
// 1001: flags 5, time 0, count 3
// 2002: flags 129, time 5, count 2
long[] perFlagWord = store.Flags(); // exact at any load
long near = store.Near(0b0000_0100, radius: 1); // items whose flags differ from 0b100 in at most 1 bit
long[] joint = store.JointCounts(HologramFaces.Flags | HologramFaces.Time); // index = word * 63 + tick
System.Console.WriteLine($"{perFlagWord[5]} {near} {joint[5 * 63 + 0]} {joint[129 * 63 + 5]}"); // 3 3 3 2
store.Diffuse(stayNumerator: 1, stayDenominator: 3); // each item stays with probability 1/3, else moves one tick
long[] perTick = store.Times(); // TimeScale (3) times the exact expected counts
System.Console.WriteLine($"scale {store.TimeScale}: {perTick[0]} {perTick[1]} {perTick[5]} {perTick[6]}"); // scale 3: 3 6 2 4
Minimal Example: Replica Reconciliation (CycleHologram, the keys-only case)
Each replica holds 10,000 items. The hologram is sized to the difference (capacity 4), not to the data.
using Phasor;
var a = new CycleHologram(capacity: 4);
var b = new CycleHologram(capacity: 4);
for (ulong id = 0; id < 10_000; id++) { a.Add(id); b.Add(id); }
a.Add(777_001); // only on A
b.Add(777_002, 2); // only on B, two copies
byte[] wire = a.Serialize(); // what A sends: 192 bytes at capacity 4 (72 bytes of field elements plus field metadata)
var delta = CycleHologram.Deserialize(wire);
delta.Subtract(b); // B subtracts its own hologram: A minus B
if (delta.TryDecode(out var diff))
foreach (var (symbol, count) in diff)
System.Console.WriteLine($"{symbol}: {count}"); // 777001: 1, 777002: -2
If the replicas differ in more than 4 distinct symbols, TryDecode returns false; size the capacity to the largest difference you expect.
Minimal Example: Approximate Bundle with Number Arithmetic
using Phasor;
// Create number faces: bind adds on the linear band
var three = PhasorCodec.NumberFace(3);
var two = PhasorCodec.NumberFace(2);
var sum = PhasorCodec.Bind(three, two);
// Decode: find which value was added
int result = PhasorCodec.DecodeSum(sum, 10); // 5
System.Console.WriteLine($"3 + 2 = {result}");
Minimal Example: Symbol Composition
using Phasor;
// Encode symbols
var role = PhasorCodec.Encode("role");
var value = PhasorCodec.Encode("agent");
// Bind: associate them
var binding = PhasorCodec.Bind(role, value);
// Correlate: check association strength
double strength = PhasorCodec.Correlate(binding, role);
System.Console.WriteLine($"Binding strength: {strength:F2}");
Table of Contents
- Pleroma: One Hologram, Many Views
- Pleroma: Flag Reads, Widening, Tenants and the Kernel's Scope
- RelationFace: Loop Holonomy in One Read
- ChargeLattice: Charges From Loop Carries
- CycleHologram: The Keys-Only Case
- KeyedSummary: Exact Counts and Sums per Key
- BitPlane: Row Bit-Planes
- Core Concepts
- Layout Constants: PhasorLayout
- Encoding: PhasorCodec.Encode & NumberFace
- The Algebra: Bind, Unbind, Bundle, Correlate
- Decoding: DecodeSum, DecodeProduct
- Advanced Cleanup: ResonatorNetworkDecode, CleanupNearest
- Hologram Type: Operators & Hot-Path Statics
- HoloCodec: SIMD Codec for Storage
- Gotchas & Invariants
Pleroma: One Hologram, Many Views
Purpose: one stored object that answers several kinds of question about the same multiset of items.
An item is a key plus, optionally, an m-bit flag word, an arrangement of k things, and a tick on a clock.
Pleroma stores the harmonic transform of the items on the product of their groups (key cycle x flag
cube Z_2^m x permutations S_k x time cycle Z_T), and only the harmonics its views need. Each view reads
its own slice of the array and nothing else, so one view stays exact while another is overloaded.
CycleHologram is the special case with keys only.
| View | What it returns | Load limit |
|---|---|---|
TryKeys |
exact total count per key | refuses past KeyCapacity distinct keys |
TryPositiveKeys |
exact count per key of a store whose every net count is zero or more | a total count of 2 x KeyCapacity per prime, over any number of keys; refuses past it |
TryItems |
whole items (key, flags, arrangement, time) and counts, when each key carries one value per factor | refuses past KeyCapacity |
Flags() |
exact count of every flag word | any load |
Near(x, r) |
exact count of items whose flags are within Hamming distance r of x | any load |
Nearest(x) |
the nearest stored flag words and their distance | any load |
Arrangements() |
exact count of every arrangement (Fourier inversion on S_k) | any load |
SignBalance() |
even arrangements minus odd ones, from one cell | any load |
Times() |
exact count at every tick (times TimeScale after Diffuse) |
any load |
JointCounts(faces) |
exact joint counts over flags, order and time (opt-in, see below) | any load |
CountWhere(predicate) |
exact count of items whose flag word satisfies any predicate, in one read | any load |
CountWhereByTime, CountWhereByArrangement |
that count per tick or per arrangement (needs the joint slice) | any load |
Attend(query, face) |
the three-face read of one query: exact counts per value, shares summing to 1, parity of the count | any load |
BitCounts(), FlagParity() |
exact count of items with each flag bit set; the XOR of every stored word, each taken count times | any load |
TryFindConflicts |
the keys whose items carry more than one flag word or arrangement | refuses past KeyCapacity |
Syndrome() |
exact count of items per Hamming syndrome (flag words of 3, 7 or 15 bits) | any load |
IsIndependent(faces) |
whether the joint counts over two or three faces are exactly the product of their marginals | any load |
Sample(rng, faces) |
one value of a view drawn with probability count / total (the store is not changed) | any load |
Density(x), Similarity(a, b), FadedTimes() |
graded key density and fading weights (continuous component only) | any load |
- Building the store:
Add(item, count),AddMany(items, counts)(validates all items first, then applies in one pass; bit-identical to sequentialAdd),Remove,Add(other)(merge),Subtract(other)(exact signed difference in every view),MergeProjection(projection, faces)(guarded merge of a projection back into the full store, refusing CheckPrime/ReservedKeys).Tick(n)ages every item by n ticks in place: the exact part turns and returns to itself after T ticks, and the continuous part also fades by r^n.SerializeandDeserializecarry the shape, fields and time scale with the data. - Shape (
HologramShape):KeyCapacity(K; key lines hold 2K + 1 numbers),FlagBits(0 to 16; the flag slice holds 2^m numbers),OrderSize(0 or 2 to 5; the order slice holds k! numbers),TimePeriod(must divide q - 1),KeyCycle(optional key range N dividing q - 1),JointFaces,Field, andContinuousFade. - Number systems (
ExactField):ExactField.Defaultis GF(2^61 - 1), whose cycle 2^61 - 2 allows time periods such as 7, 9, 25, 63 but not 27.ExactField.Prime(p)uses another prime (p below 2^40).ExactField.Primes(n, bitsEach)packs n small primes into each cell by the Chinese remainder theorem: n independent holograms per cell, n times the keys, and a prime that is over capacity refuses alone (TryKeys(component, ...)).ContinuousFade = radds a complex copy of the same cells: the classic phasor bundle, with graded similarity and fading. - Arrangements are indexed in lexicographic order:
Pleroma.ArrangementIndex(2, 0, 3, 1)andPleroma.Arrangement(index, k). - Joint views (opt-in):
JointFacestakes any combination ofHologramFaces.Flags,.Orderand.Timethat the shape has, and stores their joint slice;JointCounts(faces)then returns the exact joint counts over those faces (or any sub-combination) at any load, reading only that slice. The result is flat: index = (word * A + arrangement) * T' + tick, where A = k! if Order is chosen and T' = T if Time is chosen (1 otherwise). The single-face views equal the joint view summed over the other faces. Size: the slice holds the product of the chosen faces' orders (2^m x k! x T for all three, e.g. 10,368 cells for m = 6, S_3, T = 27), and everyAddtouches every cell, so it costs in proportion. Shapes over 2^24 joint cells are refused. Merge, Subtract, Tick, Diffuse, the relabelling operations and Serialize all keep it exact. - Superposed predicate evaluation:
CountWhere(Func<uint, bool>)counts the stored items (with their counts) whose flag word satisfies any predicate, linear or not, at any load. It reads the flag slice T once: the count is 2^-m sum_u fhat(u) T(u), where fhat is the predicate's Walsh spectrum, computed exactly in each prime's field. For repeated queries,Compile(predicate)(orFlagPredicate.Compile(m, predicate)) evaluates the predicate on all 2^m words and transforms it once.CountWhere(FlagPredicate)is then one inner product of length 2^m per prime, whatever the number of items.CountWhereByTimeandCountWhereByArrangementreturn the count per tick or per arrangement from the flags x time or flags x order joint slice. They throwInvalidOperationExceptionwhenJointFacesdoes not include both faces, and the whole family throws it when the shape has no flags. Flags are unaffected byDiffuse, soCountWhereis never scaled;CountWhereByTimeis scaled byTimeScaleand has the same limit asTimes(). The count must stay below q/2 per prime, as forFlags(). The continuous component is not read. - Diffusion in place:
Diffuse(stayNumerator, stayDenominator, steps)runs a lazy random walk over time on every item at once: each step an item stays at its tick with probability eps = a/b and moves one tick on otherwise. Each time harmonic s is multiplied by (eps + (1 - eps) zeta^s) per step, exactly in the field. Keys, flags and order views do not change;Times()and joint views with Time returnTimeScale(the product of b^steps) times the exact expected counts. Limit: that integer must stay below q/2, soTimeScalex (stored count) ⇐ (q - 1)/2; on the default field that is about 30 steps at eps = 1/3 with 5,000 items. Past it the time views throwInvalidOperationException(checked against the stored total, which is exact for stores with non-negative counts), andDiffuserefuses a scale of 2^62 or more. eps = 0 is exactlyTick(without the continuous fade). Diffused stores merge as exact expected counts, withTimeScaleset to the lcm of both. After a real diffusionTryItemsrefuses, because each key sits on more than one tick. - Relabelling the whole store in place (no items read back, at any load, every view stays exact):
RelabelFlags(bitPermutation, xorMask): every flag word has bit i moved to bitbitPermutation[i], then is XORed with the mask (any of the 2^m * m! symmetries of the cube;null= no permutation). The flag slice and the twisted key lines are permuted and sign-flipped among themselves.RelabelArrangements(left, right): every arrangement pi becomes left . pi . right (new[i] = left[pi[right[i]]]); each irrep block S becomes rho(left) S rho(right). Left and right differ.ShiftKeys(k): every key a becomes (a + k) modKeySpace; each harmonic h is multiplied by W^(k h). With several primes it needs an explicitKeyCycle, andKeyCycleand k must be multiples of the prime count (an item must stay in its prime); otherwise it throwsNotSupportedException.- Key scaling (a → u a) needs harmonic u h, which the default lines h = 0..2K do not hold: it is offered
as
ScaleKeyson the complete key band (below).
- Superposed operators: a
PleromaKernelis a weighted sum of group elements, each one a flag mask (XOR), a left arrangement relabelling (pi → sigma . pi) and a time shift; keys move only on a keyed kernel (see "The kernel is the symmetry"). Build it withnew PleromaKernel(store.Shape)andAdd(flagMask, arrangement, timeShift, weight). It is stored in harmonic form, one number per flag and time harmonic and one d x d matrix per order irrep, accumulated in the store's field.store.Apply(kernel)multiplies every stored block by its harmonic factor in one pass, so the result equals adding w copies of every item moved by every element, and its cost does not depend on how many elements the kernel holds; building the kernel costs one pass over its numbers per element. Kernels superpose (Add(otherKernel)) and compose (PleromaKernel.Compose(outer, inner), the per-harmonic matrix product: apply inner, then outer).Diffuse(a, b, steps)is this with the kernel (a stay + (b - a) one-tick shift)^steps, up to the time-scale convention:ApplyleavesTimeScaleat 1 and every cell b^steps times larger, so the time views agree and the other views are b^steps timesDiffuse's. A kernel must be built for the store's shape (same factors, field and stored harmonics), otherwiseApplythrowsArgumentExceptionand leaves the store as it was. - Bind (the group convolution of two stores):
store.Bind(other)replaces the store with every pair of items, one from each store, bound: keys add (modKeySpace), flag words XOR, arrangements compose (this store's on the left:Pleroma.ComposeArrangements(outer, inner, k), undone byInverseArrangement) and ticks add, with the counts multiplied. Every cell is a character or representation value, so this is one product per stored block (a d x d matrix product on the order irreps), one pass whatever either store holds. Binding with a one-item store shifts the whole store by that item on every face at once; binding with a many-item store applies the weighted mix of all those shifts. A kernel is the case with keys left at 0.TimeScalebecomes the product of both. Single prime only: with several primes a + a' lands in another prime's hologram, soBindthrowsNotSupportedException. - The three-face read (
Attend): the query is a flag predicate.FlagPredicate.Match(m, word, mask)is the identity match (exact with a full mask, partial on the mask's bits),FlagPredicate.Compile(m, f)any predicate or integer weight function (values within 2^30), andFlagPredicate.Combine((p1, w1), (p2, w2), ...)a weighted mix of queries, still one spectrum and one read.Attend(query, face)reads the matched items on the Flags, Order or Time face (Order and Time need the flags x that face joint slice) and returnsPleromaAttention:Countsper value and theirTotal(exact, signed),Shares= Counts / Total (summing to 1; all equal when Total is 0) andParity, +1 when Total is even and -1 when odd. Choosing a value from the shares is left to the caller. - Parity reads:
BitCounts()returns, for every bit, the exact count of items with that bit set, from m + 1 cells;FlagParity()is the XOR of every stored flag word, each taken count times, so a word stored an even number of times cancels. - The map check: when each key should carry one value,
TryFindConflicts(out keys)returns the keys whose items carry more than one flag word or more than one arrangement (exact for stores with non-negative counts per key and value, while 60 x every key total stays below q / 2), and refuses pastKeyCapacity. Times are not checked: one stored time line cannot tell one tick from a mix in a finite field. - The complete key band (
CompleteKeys = true): every key-bearing line stores all N harmonics of an explicitKeyCycleN (at most 4096, one prime) instead of 2K + 1. On it,ScaleKeys(u)maps every key a → u a mod N in place, reading harmonic u h for harmonic h: a unit mod N relabels the keys, any other u sends several keys to one and their counts add (u = 0 sends every item to key 0), andPleroma.CombineCycles(a, b)joins two stores on coprime cycles Na and Nb into one on Na x Nb: every pair of items gives the key that is the first key mod Na and the second mod Nb (the Chinese remainder theorem), with flags XOR, arrangements composed and ticks added as inBind. Without the complete band,ScaleKeysthrowsNotSupportedException: the needed harmonics are not stored. - Move one key's counts onto another (
MergeKeys(from, into), complete band): every count under keyfrommoves to keyintoin place, in one pass over the cells. Each item keeps its flags, arrangement and time, so the result equals the store built from the relabelled items, cell for cell in every view. A key that receives items with other tags holds several tag sets:TryItemsrefuses that key, whileTryKeysand the flag, arrangement and time reads stay exact.MergeKeysdoes not retag. It refuses (NotSupportedException) stores withReservedKeys, a relation face or a continuous component. - Linear bit maps (
ItemLines = false):Frobenius(modulus, steps)squares every flag word as a polynomial over GF(2) modulo the given polynomial, andShiftRegister(polynomial, steps)steps every word through a linear shift register (b(x) → x b(x) mod the polynomial). Both are exact on the flag slice, the joint slices and the continuous part at any load, and any number of steps costs one pass. A store that keeps its per-bit item lines (the default) refuses both withNotSupportedExceptionunless the map only permutes bits, because the map would send those lines to harmonics the store does not hold; build the shape withItemLines = false, which also turns offTryItemsandTryFindConflicts. Shapes with either option serialize as "PLR3"; every other store serializes exactly as before. - Composition reads:
IsIndependent(faces)tests, in exact integers, that the joint counts over two or three stored faces are the product of their marginals (independent choices).Syndrome()reads, from 2^r cells of the flag slice, how many stored words have each Hamming(2^r - 1) syndrome: position p is bit p - 1, a code word reads 0 and a single error at p reads p.Sample(rng, faces)draws one value of any view with probability count / total from one exact integer draw. - Rates round a loop (
RelationFace):AddRates(a, b, forward, backward, primes)stores a pair of positive rational rates (a → b and b → a), each a (numerator, denominator) that factors over the given primes, as one exponent per prime (one tag per prime).Current(loop, primes)returns +1, -1 or 0: whether the product of the rates along the loop is larger, smaller or equal to the product against it, decided exactly with whole numbers (no logarithm is rounded). For a chain with one loop this is the direction of its steady-state current; a chain is reversible exactly when every fundamental loop reads 0 (Kolmogorov's criterion). - Time-valued maps (
TimeSpectrum = true): the store keeps a key line for every time harmonic, soTimeSpectrum()returns each key's exact tick histogram,TimeMap()the one tick each key maps to (or -1 when its stored times are a mix),TryFindConflictsalso reports keys with more than one tick, andMapTimes(map)applies any map of the ticks (map[t] is t's image, a bijection or not) to every item in place, which composes the stored key → tick map with it.MapTimesalso works on a store without item lines. The key views refuse past capacity as always. Joining two stored maps (one store's ticks matched to another's keys) is not offered: it pairs two different factors, which no product of stored harmonics can do. - Relations in the same store (
RelationKeys,RelationModulus,RelationTags): the store carries a relation face over its own small set of relation keys, asRelationFacestores it.AddRelation(a, b, offset, tag)stores "b is offset ahead of a";RelationLoops(tag)gives the fundamental loops;Holonomy(loop)reads a loop's winding mod n,Frustration(loop)its parity, andReadLoop(loop)the integer sum;AddRatesandCurrent(loop, primes)give the exact circulation direction of a loop of rational rates. The relation face merges and subtracts with the store, serializes with it and is cleared with it; operations on items (ageing, relabelling, shifts, kernels,Bind) leave it as it is, andCombineCyclesrefuses a store that has one. - Dense pages (
CompleteKeys), for database use:Count(key)andCounts()read any slot of a complete key band exactly at any load (one mixed-radix inverse transform for the whole page);Total()reads the total of any store from one cell.Pleroma.FromCounts(shape, counts)andAddCounts(counts)build or add a whole page from a dense count vector in one forward transform (the same cells as oneAddper key);AddFlagCounts(histogram, key)does the same for a histogram of flag words.KeyPredicate(Compile,Range,Match,Combine) withCountWhereKeysums any function of the key over a page in one read;MinKey,MaxKeyandKeyAtRankgive order statistics;Pleroma.InnerProduct(a, b)is the exact sum of a's counts times b's on equal keys (an equi-join count or sum) in one read. A truncated key band refuses these reads: it lacks the harmonics. - Shadows, snapshots and raw pages:
Project(faces)returns the smaller store holding only the key factor and the named faces, equal to the store of the items with the other faces dropped.Clone()is a deep copy for copy-on-write snapshots: reads are safe from many threads on a store no thread is writing, writes need exclusive access, and per-thread delta stores merge withAdd(other)in any order.RawCellsandLoadCellsmove the exact cells without a fullSerialize. - Modulation and key-axis products (dense pages):
ModulateKeys(l)multiplies the count at every key a by W^(l a) (the partner ofShiftKeys: shifting by k and modulating by l commute up to the exact scalar W^(k l)),MultiplyKeys(weights)multiplies the count at every key by its weight,Hadamard(other)weights this store by the other's key counts, andApplyBank(shifts, diagonals)applies sum_k D_k T^(shift_k). These need every face slice to be a key line: keys only, orFlagSpectrum/TimeSpectrumfor flags and time; otherwise they refuse. - SQL-style compositions, exact:
Restrict(predicate)keeps only the items whose flag word satisfies a 0/1FlagPredicate(a predicate that is not an affine sign pattern needsFlagSpectrum);Support()returns the page with one item at every key present (itsTotal()is COUNT DISTINCT);MapCounts(f)returns the page of f applied to every key's count. Sums per group areMultiplyKeysby the value followed byScaleKeys(0)(value on the key, group on the flags); joins areCombineCycles(disjoint flag bits) followed byMultiplyKeyswith the join condition. Measured: each of these four recipes equals brute force on random signed stores in three fields (30/30, 18/18, 24/24, 18/18), and each one-pass operation equals its recipe cell for cell. - Compact stores and page sets: every store of a shape shares one registry and one set of transform tables, so a
store costs its cells (a 14-bit flag plane: 8.02 bytes per cell).
PleromaPageSetholds many dense pages of one shape as contiguous cells, addressed by index or by a long key (GetOrAddPage,TryGetPage), withAddPage,AddCounts,Add,Count,Counts,Total,IsZero,Sample,ToPleroma,Serialize, and batched reads (CountsInto,CountsOfKeys).Pleroma.FromCellsandFromCellsManyload stores from raw cells. Measured: 53,031 pages of 110 keys in 49.6 MB; a whole 110-key page reads in 3 to 4 microseconds. - Batch flag words, one pass:
AddFlagWords(words, counts, key)adds k signed flag words in one pass over the cells, equal cell for cell to kAddcalls (a CheckPrime mirror included);Pleroma.AddFlagWords(stores, words, counts)does the same for several stores of one shape.Flags(bits)returns the exact count of each flag word on a bit subset read straight from the flag slice, equal toProjectFlags(bits).Flags()with no store built.Pleroma.FlagsMany(stores, bits)reads that subset from several stores of one shape in one batched transform per prime (one row per store), cell for cell equal to oneFlags(bits)per store. - More dense-page operations:
SampleKeydraws a key with probability equal to its exact share;ProjectFlags(bits)keeps a subset of flag bits (the others summed out);Pleroma.Add(stores, item, counts)adds one item to several stores at once;MultiplyTimes(weights)multiplies every count by a weight per tick (0/1 weights delete ticks), andTimeWeightedCounts(weights)sums any weighting of the ticks per key in one read. Together they give an exactly cancelling, age-fading weight: an event is an item at tick 0, each tick ages every item and deletes those past the horizon, and the read with weights max(0, H - age) is the faded sum per key. - Redundancy, one kind per face:
HologramShape.ReservedKeyskeeps r keys empty, soVerify()detects up to r corrupted harmonics per line,Repair(lostHarmonics)rebuilds up to r lost ones exactly andRepair()corrects up to r / 2 at unknown positions.HologramShape.CheckPrimekeeps a mirror of every cell in a second prime;VerifyCheck()detects any corruption confined to one copy (persist both withRawCellsandRawCheckCells). Superposed kernels (Apply(kernel)) run on both copies: the kernel carries its check-prime mirror, built from the same elements. A wrap of a value past the main field's range is caught byVerifyCheck()while the true value stays inside both ranges.PleromaParitykeeps the exact sum of a group of stores, updated incrementally (Update(before, after)), and rebuilds any one lost store; a page set keeps the same parity withparity: true. Measured: lost harmonics rebuilt 300/300 for each of 1 to 5 with r = 5; corruptions detected 1,000/1,000 for each of 1 to 5; 2 at unknown positions corrected 300/300; one corrupted cell in either copy detected 10,000/10,000; any one lost store rebuilt 100/100. - Sparse pages for large key sets:
PleromaSparsePageholds exact counts over keys anywhere in 0 .. 2^61 - 3 in memory proportional to the keys present: its cells are a keys-only Pleroma's truncated key band (capacity growing by doubling), and it keeps the keys it holds, soTotalandCount(one key) need no full decode;CountsandSampleKeysolve every count.FromCounts(pairs)builds a page from (key, count) pairs in one batch, identical to adding them one by one (about 1.1 to 1.7 times faster at 64 to 4,096 keys on the benchmark machine).Add,Subtractand merges are exact;RawCells/RawKeysandFromCellspersist it, andFromCells(cells)recovers the keys from the cells alone. Measured over 36,922 subword keys: 183 bytes and 0.4 microseconds per read for one key, about 2.2 KB and 19 to 41 microseconds for 50 keys (a dense page of that key set would be 301 KB). - Arrangements of many things:
PleromaArrangementBand(elements, order)holds arrangements of any number of things in Width x Width exact cells (Width = elements x (elements - 1) x ... overorderfactors, at most 4,096) and answers every question that looks at up toorderplaces or elements:Count(elements, places),PlaceCounts,Scores(placeWeights)(any positional score, order 1),BeforeCounts(the pairwise matrix, order 2), andCountWhereAt/CountWhereOffor any test on that many places or elements; a question that looks further is refused.Add(other)merges,RelabelElements,RelabelPlaces,InvertandBind(compose every pair, counts multiply) run in one pass,ToOrderdrops to a lower order, andFromArrangementCountsreads a Pleroma'sArrangements().Verify()is true exactly when some assignment of counts to arrangements has those cells;TryRepair()restores one changed cell. Twenty things at order 2 take 144,400 cells; one count per arrangement would take 2,432,902,008,176,640,000. Measured: 20 candidates, 600 ballots in 6 merged precincts, every positional score and pairwise count equal to direct counting. - Words of up to 64 flags:
PleromaFlagBand(flagBits, order)holds flag words of up to 64 flags in 1 + C(flagBits, 1) + ... + C(flagBits, order) exact cells and answers every question that looks at up toorderflags:Count(mask, value),Flags(bits)(the cross-tabulation of those flags),CountWhere(bits, test)for any test,BitCounts,PairCounts(order 2),Parity(mask), andDistanceMoments(word)(the sum over the items of the distance to any word, to every power up toorder); a question that looks further is refused.Add(other)merges,RelabelFlags(reorder and flip) andBind(XOR every pair, counts multiply) run in one pass,ProjectFlagskeeps chosen flags,ToOrderdrops to a lower order, andFromFlagCountsreads a Pleroma'sFlags().Restrict(bits, test)keeps only the items that pass a test on n flags and returns the band of orderorder- n of those items: a window that reads n flags costs n orders. Sixty-four flags at order 3 take 43,745 cells (349,960 bytes); one count per word would take 18,446,744,073,709,551,616. Measured: 64 flags, 10,000 words in 8 merged shards, every pair count, 3,200 three-flag cross-tab counts and the distance moments equal to direct counting. - A proof that a read is exact, from two numbers: counts live modulo a prime, so a read is the true number
while the true number stays below half the prime. Every store carries
Mass(an upper bound on the sum of |count| over its items) andPeak(an upper bound on the sum over the items of any one key), and every operation moves them by its own rule: a merge adds, a bind multiplies, a kernel scales by the sum of its |weights|, a key scale by the number of keys that fall together.ReadsCertifiedproves every count and every marginal exact (Mass timesTimeScaleis within half the smallest prime),KeyReadsCertifieddoes the same for reads inside one key,WeightedReadsCertified(maxWeight)for weighted sums, andPleroma.InnerProductCertified(a, b)proves a join count exact from the two stores' own numbers, with no pass over either (the smaller of Mass(a) x Peak(b) and Peak(a) x Mass(b)).trueis a proof;falsemeans the store cannot tell. A store built from raw cells has no bound (Massis null) untilLoadCells(cells, timeScale, mass, peak)restores the numbers it was saved with;Serializecarries them.Peakis far belowMasson a store of many keys built from whole pages (FromCounts,AddCounts). - Streamed least squares per harmonic:
HarmonicLeastSquares(harmonics, features)accumulates complex samples (Accumulate,Mergeacross shards) andSolve(relTol)returns per harmonic the weights, or refuses a singular harmonic (never zero-filled), with the residual.HarmonicLeastSquaresExactdoes the same with integers mod a prime, exactly, so shards merge in any order to the same bits. Measured: weights equal a dense least squares to 2e-15 relative; the refused harmonics are exactly the singular ones. - Causal reads, adjoints, a float export and soft weights:
CausalReads(items, faces | FlagPredicate | KeyPredicate)returns a view after each prefix of an item sequence (one read plus one update per item);PleromaKernel.Adjoint()is the transpose of a superposed operator (the kernel of the inverse elements), andFrobenius/ShiftRegisteraccept negative steps when the map is invertible;Pleroma.FlagInnerProduct(a, b)pairs two flag views;ToComplex()exports a dense page's exact counts as their phasor-bundle harmonics (one way);FlagPredicate.QuantizeandFlagPredicate.Softmaxturn real weights into exact integer weights with a stated error bound (QuantizedFlagPredicate.MaxWeightError), soAttendgives softmax-weighted shares exactly for the quantised weights. Measured: the shift/modulation relation holds exactly on every cell 30/30; causal reads equal adding and reading after every prefix 290/290; the adjoint equals the kernel of the inverse elements 20/20; quantised reads stay within the stated bound 40/40. - Learning over GF(2) (
Eliminate): with one flag bit used as a label, every stored word is an example (input, label), andEliminate(labelBit)solves, by Gaussian elimination over GF(2), for the rule label = parity(input AND Mask) XOR Constant that fits every example. It returns aFlagRule(Mask,Constant,Uniquewhen the examples determine the rule,Evaluate(word),ToPredicate()forCountWhereandAttend), or null when no such rule fits (for example, one input stored with both labels). The examples are read from the flag view, so they are exact at any load and are added and removed like any item; inputs are at most 15 bits. - The graded operator tower (
PleromaKernel.Graded): layer i of a flag cube of n bits (n at most 12) gets a flip of bit i or a quarter-turn (the flip after bit i's sign, which squares to -1), times the sign of all earlier layers (the Jordan-Wigner exchange sign), so each new layer anticommutes with every earlier one.Graded(layers)returns the generators asFlagOperators (exact signed permutations of the flag words, which compose with*and act on a count vector withApply) and the generated algebra's dimension, centre, trace-form signature and type (real, complex, quaternion, or a pair). The signs cannot be added to a kernel; the generators act on count vectors such asFlags()withApply(counts), and on stores withPleroma.Apply(FlagOperator)(below). - Signed permutations of the flag words on a store (
Pleroma.Apply(FlagOperator)): every stored item with flag word b moves to the operator's image of b and its count is multiplied by the operator's sign at b, exactly, every view included.FlagOperator.FromPermutation(m, permutation, signs)builds any such operator. Flips and bit permutations apply on any store. A general signed permutation, or a sign over two or more bits, needs every key line to hold the whole flag cube: build the shape withFlagSpectrum = true(with an order face, alsoItemLines = false); otherwiseApplythrowsNotSupportedExceptionand leaves the store as it was. Measured: equal to the store of the moved, re-signed items 40/40, and for each of the ten symmetry classes, every realisation of its time reversal, particle-hole and chiral operators on 3 flag bits (2,079 in all) satisfies its squares and (anti)commutation relations on a whole store of signed items: 11,995 checks, 10/10 classes. - Measured (the package test suite, each API checked against a rebuilt store, brute force or a naive transform): keys exact 30/30
at 16 keys with 1,000 items and refused 30/30 at 17 and 20; flags, near-counts, nearest words, arrangements,
sign and times exact at 200 keys while the key view refuses; whole items exact 30/30; every view unchanged
when all cells outside its slice are scrambled; 192 items from 129 cells with three 17-bit primes, 30/30;
S_2 to S_5 representations verified as homomorphisms on every pair. Relabelling equals a store rebuilt from
the relabelled items, cell for cell and view for view: all 384 flag symmetries at m = 4, all 24 x 24
left/right pairs on S_4, every key shift on a 257-key cycle, on the default field and with three primes.
Joint views: flags x time exact 30/30 and flags x order x time exact 30/30 at 1,000 items on 200 keys
(1,728 and 10,368 cells); unchanged under scrambling 20/20. Diffusion at eps = 1/3: 600/600 steps equal
the exact expected counts, and eps = 0 equals
Tick60/60. Predicates: a majority of block parities plus 100 random predicates, m = 10 to 12, up to 5,000 items, equal to direct counting 1,515/1,515 on three fields; per tick 141/141 (including Tick, Diffuse and Merge) and per arrangement 126/126. Superposed operators:Applyequals the store built from every moved item, every cell and every view, for kernels of 1, 10, 100 and 1,000 elements and the whole group of 864, 16/16 per field on the default field and on three primes, and 7/7 on q = 13,879, m = 6, S_3, T = 27 with a continuous component;Composeequals sequentialApply20/20;Diffusematches the two-element kernel 24/24.Applyon a 10,464-cell store took 0.17 to 0.44 ms per call over two runs, with no trend from 1 to 10,368 elements, while building the same store from the moved items took 3 ms at 1 element and 1,916 ms at 1,000.Bindequals the store built from every pair, cell for cell and view for view, 35/35 across five shapes (S_3 to S_5, a continuous component, a store bound with itself); all 10,000 sequences of 4 symbols from 10, bound, give 715 distinct keys (the multisets) and 256 distinct flag words (the odd-occurrence sets), each item exact.Attendequals direct counting 576/576 over three fields with exact, partial, arbitrary and mixed queries, and its parity reads the Hamming(7,4) syndrome of all 112 single errors.TryFindConflictsfound exactly the corrupted keys 90/90 with no false alarms on 90/90 clean maps.ScaleKeysequals the store rebuilt from the scaled keys for all 120 units of 225 on two fields and for all 113 non-units of 225 and 16;CombineCyclesequals the store of Chinese-remainder pairs 12/12;FrobeniusandShiftRegisterequal the stores of mapped words 18/18 and 9/9, with the field automorphism of order exactly 8 on GF(2^8) and register periods of exactly 255 and 63 for primitive polynomials (8 for x^8 + 1);Syndromelocates all 112 single errors of Hamming(7,4) and sends all 336 double errors to a wrong third position;Currentequals the sign of the exact rational steady-state current on 200/200 rings of 3 to 6 states, and reads 0 on every loop of 30/30 reversible networks.Eliminaterecovers random affine rules exactly from just enough independent examples 180/180, agrees with a brute-force search over every affine rule on 450/450 example sets (188 of them with no rule), and learns a 3-bit parity of 15 inputs from 60 examples 20/20.Gradedgives, for n = 0 to 12 flip layers, the types R, R+R, R, C, H, H+H, H, C, R, R+R, R, C, H (period exactly 8); without the sign the algebra is commutative at every n; and with p flips and q quarter-turns the type depends only on (p - q) mod 8, 49/49.TimeSpectrumequals the per-key tick histograms 37/37 on three fields (signed stores, after ageing, merging and diffusion),TimeMap60/60 with every time-only conflict found, andMapTimesequals the store rebuilt with the mapped ticks 18/18; the relation face in the store equals a standaloneRelationFaceon 2,176/2,176 loop reads and on 1,804/1,804 loops of merged and subtracted stores. Dense pages:Counts()equals the stored counts at N = 9, 225, 2925 and 3150 and takes 188 to 246 ns per slot at N = 2925 and 3150 (0.6 to 0.7 ms per page);AddCountsequals oneAddper key 15/15 and costs about 760 ns per slot on a 3,150-slot page against about 80 us per slot for oneAddper key;CountWhereKeyequals direct counting 36/36;InnerProduct10/10;Projectequals the store of projected items 21/21.PleromaKernel.Graded(layers, monadSign, centralQuarterTurn: true)adds a central quarter-turn, and the tower then alternates between the two complex classes (C, C+C) with period 2;GradedAlgebra.SymmetryClass(n, complex)names each step's Altland-Zirnbauer class, andGradedAlgebra.Generate(generators)gives the same invariants for any signed permutations of the flag words (null when they cannot be classified). Built from J, T, C and S on three flag bits, every realisation of each of the ten classes (from 1 for A to 720 for BDI) generates its class's number field.
using Phasor;
var store = new Pleroma(new HologramShape
{
KeyCapacity = 16, // whole items come back for up to 16 distinct keys
FlagBits = 8, // an 8-bit flag word per item
OrderSize = 4, // an arrangement of 4 things per item
TimePeriod = 63, // a 63-tick clock (on the default field it must divide 2^61 - 2)
});
int order = Pleroma.ArrangementIndex(2, 0, 3, 1);
store.Add(new HologramItem(Key: 1001, Flags: 0b0000_0101, Arrangement: order, Time: 0), 3);
store.Add(new HologramItem(Key: 2002, Flags: 0b1000_0001, Arrangement: 0, Time: 5), 2);
store.Tick(10); // every item ages 10 ticks, in place
if (store.TryItems(out var items))
foreach (var (item, count) in items)
System.Console.WriteLine($"{item.Key}: flags {item.Flags}, arrangement {item.Arrangement}, time {item.Time}, count {count}");
// 1001: flags 5, arrangement 13, time 10, count 3
// 2002: flags 129, arrangement 0, time 15, count 2
long[] perFlagWord = store.Flags(); // perFlagWord[5] == 3, perFlagWord[129] == 2
long nearby = store.Near(0b0000_0100, radius: 1); // 3: items whose flags differ from 0b100 in at most 1 bit
long[] perTick = store.Times(); // perTick[10] == 3, perTick[15] == 2
long evenMinusOdd = store.SignBalance(); // -1: three odd arrangements, two even
System.Console.WriteLine($"{perFlagWord[5]} {perFlagWord[129]} {nearby} {perTick[10]} {perTick[15]} {evenMinusOdd}");
Relabelling in place:
using Phasor;
var store = new Pleroma(new HologramShape { KeyCapacity = 8, FlagBits = 4, OrderSize = 3 });
store.Add(new HologramItem(Key: 10, Flags: 0b0011, Arrangement: 1), 2); // arrangement 1 = (0, 2, 1)
store.RelabelFlags(new[] { 1, 2, 3, 0 }, xorMask: 0b0001); // 0b0011 -> 0b0110 -> 0b0111
store.RelabelArrangements(left: 0, right: 3); // (0, 2, 1) . (1, 2, 0) = (2, 1, 0)
store.ShiftKeys(5); // key 10 -> 15
if (store.TryItems(out var items))
foreach (var (item, count) in items)
System.Console.WriteLine($"{item.Key}: flags {item.Flags}, arrangement {item.Arrangement}, count {count}");
// 15: flags 7, arrangement 5, count 2
Counting by any predicate over a superposed store:
using Phasor;
var store = new Pleroma(new HologramShape
{
FlagBits = 10, // 10-bit flag words
TimePeriod = 9,
JointFaces = HologramFaces.Flags | HologramFaces.Time, // needed only for CountWhereByTime
});
var rng = new System.Random(1);
for (int i = 0; i < 5000; i++) // 5,000 items, far past KeyCapacity (16)
store.Add(new HologramItem(Key: (ulong)i, Flags: (uint)rng.Next(1024), Time: i % 9));
// any predicate on the flag word, linear or not, compiled once (its Walsh spectrum) and reused
FlagPredicate majority = store.Compile(w => System.Numerics.BitOperations.PopCount(w) >= 6);
long count = store.CountWhere(majority); // exact, one inner product with the flag slice
long[] perTick = store.CountWhereByTime(majority); // exact count per tick from the joint slice
long oneOff = store.CountWhere(w => (w & 0x200) != 0 && (w & 0x3) == 0); // compiles and reads in one call
long[] words = store.Flags(); // the same answers, summed from the full flag view
long check = 0;
for (uint w = 0; w < 1024; w++) if (System.Numerics.BitOperations.PopCount(w) >= 6) check += words[w];
System.Console.WriteLine($"{count} {check} {perTick[0]} {oneOff} {store.TryKeys(out _)}");
// 1870 1870 226 626 False
A superposed operator, applied in one pass:
using Phasor;
var shape = new HologramShape { KeyCapacity = 8, FlagBits = 4, OrderSize = 3, TimePeriod = 9 };
var store = new Pleroma(shape);
store.Add(new HologramItem(Key: 10, Flags: 0b0011, Arrangement: 1, Time: 2), 2);
var op = new PleromaKernel(shape); // any number of group elements, weighted
op.Add(weight: 1); // stay where it is
op.Add(flagMask: 0b0001, timeShift: 1, weight: 2); // flip bit 0 and move one tick, two copies
store.Apply(op); // one pass over the store's cells
long[] perTick = store.Times();
long[] perWord = store.Flags();
System.Console.WriteLine($"{perTick[2]} {perTick[3]} {perWord[0b0011]} {perWord[0b0010]}"); // 2 4 2 4
var twice = PleromaKernel.Compose(op, op); // op applied twice, as one kernel
The three-face read, a bind and the parity reads:
using Phasor;
var shape = new HologramShape
{
KeyCapacity = 8, FlagBits = 4, TimePeriod = 9,
JointFaces = HologramFaces.Flags | HologramFaces.Time, // Attend on the Time face reads this slice
};
var store = new Pleroma(shape);
store.Add(new HologramItem(Key: 10, Flags: 0b0011, Time: 2), 3);
store.Add(new HologramItem(Key: 20, Flags: 0b0111, Time: 5), 1);
store.Add(new HologramItem(Key: 30, Flags: 0b1000, Time: 5), 2);
// the three-face read of one query: items whose two low bits are 11, read per tick
var query = FlagPredicate.Match(4, word: 0b0011, mask: 0b0011);
PleromaAttention read = store.Attend(query, HologramFaces.Time);
System.Console.WriteLine($"{read.Counts[2]} {read.Counts[5]} {read.Total} {read.Shares[2]:F2} {read.Parity}"); // 3 1 4 0.75 1
// bind the whole store with a one-item store: every key + 1, every word XOR 0b0001, every item one tick on
var shift = new Pleroma(shape);
shift.Add(new HologramItem(Key: 1, Flags: 0b0001, Time: 1));
store.Bind(shift);
if (store.TryItems(out var items))
foreach (var (item, count) in items)
System.Console.WriteLine($"{item.Key}: flags {item.Flags}, time {item.Time}, count {count}");
// 11: flags 2, time 3, count 3
// 21: flags 6, time 6, count 1
// 31: flags 9, time 6, count 2
System.Console.WriteLine($"{string.Join(",", store.BitCounts())} {store.FlagParity()}"); // 2,4,1,2 4
Key scaling, the Chinese remainder, bit maps, syndromes, independence, a draw, and the direction of a loop of rates:
using Phasor;
// key scaling and the Chinese remainder need the complete key band (every harmonic of a small key cycle)
var nine = new Pleroma(new HologramShape { KeyCapacity = 4, KeyCycle = 9, CompleteKeys = true });
var other = new Pleroma(new HologramShape { KeyCapacity = 2, KeyCycle = 25, CompleteKeys = true });
nine.Add(2, 3); // key 2, three copies
nine.ScaleKeys(4); // a -> 4a mod 9: key 8
other.Add(11);
var both = Pleroma.CombineCycles(nine, other); // keys on the cycle of 225: the x with x = 8 mod 9 and x = 11 mod 25
both.TryKeys(out var keys);
System.Console.WriteLine(string.Join(" ", keys)); // (161, 3)
// linear bit maps on the flag cube: a store without per-bit item lines
var cube = new Pleroma(new HologramShape { FlagBits = 4, ItemLines = false });
cube.Add(new HologramItem(Key: 1, Flags: 0b0110), 2);
cube.Frobenius(modulus: 0b1_0011); // b(x)^2 mod x^4 + x + 1: x^2 + x -> x^4 + x^2 = x^2 + x + 1
cube.ShiftRegister(polynomial: 0b1_0011, steps: 15); // a full period of a primitive register: back where it was
long[] words = cube.Flags();
System.Console.WriteLine($"{words[0b0111]}"); // 2
// Hamming(7,4) syndromes, independence, and the read-time draw
var code = new Pleroma(new HologramShape
{
FlagBits = 7, TimePeriod = 9, JointFaces = HologramFaces.Flags | HologramFaces.Time,
});
code.Add(new HologramItem(Key: 1, Flags: 0b0000111, Time: 0)); // a code word
code.Add(new HologramItem(Key: 2, Flags: 0b0000111 ^ 0b10000, Time: 0)); // the same word, error at position 5
long[] perSyndrome = code.Syndrome();
bool independent = code.IsIndependent(HologramFaces.Flags | HologramFaces.Time);
int word = code.Sample(new System.Random(1), HologramFaces.Flags);
System.Console.WriteLine($"{perSyndrome[0]} {perSyndrome[5]} {independent} {word == 0b0000111 || word == 0b0010111}"); // 1 1 True True
// the circulation direction of a loop of rates (Kolmogorov's criterion), exact on rational rates
var rates = new RelationFace(keys: 3, modulus: 2, tags: 2);
long[] primes = { 2, 3 };
rates.AddRates(0, 1, forward: (32, 1), backward: (1, 1), primes);
rates.AddRates(1, 2, forward: (1, 1), backward: (1, 1), primes);
rates.AddRates(2, 0, forward: (1, 1), backward: (27, 1), primes); // round 0 -> 1 -> 2 -> 0: 32 against 27
foreach (var loop in rates.FundamentalLoops())
System.Console.WriteLine($"{rates.Current(loop, primes) * loop.EdgeCoefficients[0]}"); // 1: net current runs 0 -> 1 -> 2
Learning a rule over GF(2), and the graded tower:
using System.Linq;
using Phasor;
// learning over GF(2): bit 4 of each flag word is the label, bits 0-3 the input; the hidden rule is bit0 XOR bit2 XOR 1
var examples = new Pleroma(new HologramShape { FlagBits = 5 });
foreach (uint x in new uint[] { 0b0000, 0b0001, 0b0010, 0b0100, 0b1000, 0b0111 })
{
bool label = ((x & 1) ^ ((x >> 2) & 1) ^ 1) == 1;
examples.Add(new HologramItem(Key: x, Flags: x | (label ? 1u << 4 : 0)));
}
FlagRule? rule = examples.Eliminate(labelBit: 4);
System.Console.WriteLine($"mask {rule!.Mask} constant {rule.Constant} unique {rule.Unique} f(0b0101) {rule.Evaluate(0b0101)}"); // mask 5 constant True unique True f(0b0101) True
examples.Add(new HologramItem(Key: 99, Flags: 0b0000)); // input 0000 with label 0: contradicts the label 1 already stored
System.Console.WriteLine(examples.Eliminate(labelBit: 4) is null); // True: no affine rule fits
// the graded tower: n flip layers with the monad's exchange sign cycle through 8 algebra types
foreach (int n in new[] { 1, 3, 4, 8, 9 })
{
GradedAlgebra a = PleromaKernel.Graded(Enumerable.Repeat(GradedLayer.Flip, n).ToArray());
System.Console.WriteLine($"{n}: dim {a.Dimension} centre {a.Centre} {a.Type}");
}
// 1: dim 2 centre 2 RealPair
// 3: dim 8 centre 2 Complex
// 4: dim 16 centre 1 Quaternion
// 8: dim 256 centre 1 Real
// 9: dim 512 centre 2 RealPair
Time-valued maps and relations in the same store:
using System.Linq;
using Phasor;
var store = new Pleroma(new HologramShape
{
KeyCapacity = 8, TimePeriod = 9,
TimeSpectrum = true, // a key line per time harmonic: exact tick histograms per key
RelationKeys = 4, RelationModulus = 257, // and a relation face over relation keys 0..3
});
store.Add(new HologramItem(Key: 10, Time: 2), 3); // key 10 maps to tick 2
store.Add(new HologramItem(Key: 20, Time: 5), 1);
store.Add(new HologramItem(Key: 20, Time: 7), 1); // key 20 is a mix of ticks 5 and 7
foreach (var (key, tick, count) in store.TimeMap()!)
System.Console.WriteLine($"{key}: tick {tick}, count {count}");
// 10: tick 2, count 3
// 20: tick -1, count 2
store.MapTimes(Enumerable.Range(0, 9).Select(t => t * 2 % 9).ToArray()); // every tick t -> 2t mod 9
var spectrum = store.TimeSpectrum()!;
System.Console.WriteLine(string.Join(" ", spectrum[1].Ticks)); // key 20: ticks 1 and 5 hold 1 each
// 0 1 0 0 0 1 0 0 0
store.AddRelation(0, 1, 5);
store.AddRelation(1, 2, 7);
store.AddRelation(0, 2, 13); // one more than 5 + 7: the loop does not close
foreach (var loop in store.RelationLoops())
System.Console.WriteLine($"winding {store.Holonomy(loop)} frustration {store.Frustration(loop)}"); // winding 256 frustration 1 (the loop sum is -1)
Dense pages, shadows and the class labels:
using System.Linq;
using Phasor;
// a dense page: every harmonic of a key cycle of 225 slots, so any slot reads back exactly at any load
var page = new HologramShape { KeyCapacity = 4, KeyCycle = 225, CompleteKeys = true };
long[] values = Enumerable.Range(0, 225).Select(a => (long)(a % 7) - 3).ToArray(); // a column of 225 small integers
var a = Pleroma.FromCounts(page, values); // one forward transform, the same cells as 225 Add calls
System.Console.WriteLine($"{a.Count(10)} {a.Counts()[100]} {a.Total()}"); // 0 -1 -3
System.Console.WriteLine(a.CountWhereKey(KeyPredicate.Range(225, 0, 6))); // -3 + -2 + ... + 3 = 0
System.Console.WriteLine(a.CountWhereKey(KeyPredicate.Compile(225, k => k % 7 == 6))); // 32 slots holding 3: 96
var b = Pleroma.FromCounts(page, Enumerable.Range(0, 225).Select(k => k < 10 ? 1L : 0L).ToArray());
System.Console.WriteLine(Pleroma.InnerProduct(a, b)); // sum of a over slots 0..9: -6
// a shadow keeps only the faces a query class needs
var cube = new Pleroma(new HologramShape { KeyCapacity = 4, FlagBits = 4, TimePeriod = 9 });
cube.Add(new HologramItem(Key: 1, Flags: 5, Time: 2), 3);
Pleroma flagsOnly = cube.Project(HologramFaces.Flags);
System.Console.WriteLine($"{flagsOnly.Flags()[5]} {flagsOnly.CellCount} < {cube.CellCount}"); // 3 56 < 72
System.Console.WriteLine(GradedAlgebra.SymmetryClass(4, complex: false)); // AII
The balance names used in this project: syzygy (0 = (+1) + (-1), the cancelling pair: the key cycle and its counts), monad (1 = 0 + 1, selection: the order face), ouroboros (0 = 1 + 1, self-cancellation: the flag cube).
Prior art: Fourier analysis on finite groups, Reed-Solomon/BCH decoding, Krawtchouk polynomials.
Pleroma: Flag Reads, Widening, Tenants and the Kernel's Scope
The flag face of a store is the m-bit flag word of every item. The operations below read or write that face for a few words, for several stores, or for several bit subsets, without building anything extra. Each example was compiled and run against this package, and its output is pasted under it. The examples build in a console project with ImplicitUsings enabled, which is the SDK default.
Exact counts on chosen flag bits: Flags(bits)
Flags(bits) returns the exact count of each flag pattern on the bits you name, summing out every other bit. Index u of
the result has bit j set when flag bit bits[j] is set. The empty list returns the total. No store is built: the cost is
a gather of 2^k cells and one transform of that size, where k is the number of bits chosen.
using Phasor;
var store = new Pleroma(new HologramShape { FlagBits = 3 });
store.Add(new HologramItem(Key: 1, Flags: 0b101), 2); // bits 0 and 2 set
store.Add(new HologramItem(Key: 2, Flags: 0b100), 1); // bit 2 only
store.Add(new HologramItem(Key: 3, Flags: 0b011), 4); // bits 0 and 1 set
// Marginal on flag bits 0 and 2, with no store built: index u has bit 0 = flag bit 0, bit 1 = flag bit 2.
long[] onBits02 = store.Flags(new[] { 0, 2 });
System.Console.WriteLine($"bits {{0,2}}: {string.Join(" ", onBits02)}");
// The empty list is the total.
System.Console.WriteLine($"bits {{}}: {string.Join(" ", store.Flags(Array.Empty<int>()))}");
Output:
bits {0,2}: 0 4 1 2
bits {}: 7
Several stores, one bit set; several bit sets: FlagsMany and FlagsUnion
Pleroma.FlagsMany(stores, bits) returns one result per store. The stores must share a shape. They are transformed in one
batched transform per prime, so reading many planes costs one batched transform per prime rather than one per plane. Each
row equals Flags(bits) of that store, cell for cell. In the recorded run the batched read took 0.39 to 0.62 of the per-plane time (4 to 49 planes, 10 bits of a 14-bit plane, loaded machine). The time still grows with the number of planes, because the output does.
Pleroma.FlagsUnion(stores, bitSets) returns a jagged array indexed [bit set][store]. By default each bit set is its own
batched transform per prime, with the stores as lanes (the FlagsMany route), and each answer equals the single call for that set.
using Phasor;
var monday = new Pleroma(new HologramShape { FlagBits = 3 });
var tuesday = new Pleroma(new HologramShape { FlagBits = 3 });
monday.Add(new HologramItem(Key: 1, Flags: 0b101), 2);
tuesday.Add(new HologramItem(Key: 1, Flags: 0b100), 6);
// One bit set, many stores: result[store] is the marginal on bit 2 of that store.
long[][] onBit2 = Pleroma.FlagsMany(new[] { monday, tuesday }, new[] { 2 });
System.Console.WriteLine($"FlagsMany bit 2: monday {string.Join(" ", onBit2[0])} | tuesday {string.Join(" ", onBit2[1])}");
// Many bit sets, many stores: result[bitSet][store].
long[][][] union = Pleroma.FlagsUnion(new[] { monday, tuesday }, new[] { new[] { 0 }, new[] { 1, 2 } });
System.Console.WriteLine($"FlagsUnion bit 0: monday {string.Join(" ", union[0][0])} | tuesday {string.Join(" ", union[0][1])}");
System.Console.WriteLine($"FlagsUnion bits 1,2: monday {string.Join(" ", union[1][0])} | tuesday {string.Join(" ", union[1][1])}");
Output:
FlagsMany bit 2: monday 0 2 | tuesday 0 6
FlagsUnion bit 0: monday 0 2 | tuesday 6 0
FlagsUnion bits 1,2: monday 0 0 2 0 | tuesday 0 0 6 0
Several reads in one call: PleromaReadSet
Declare each read once with AddFlagMarginal(bits), AddTotal(), AddBitCounts(), AddFlagParity() or
AddFlagWordCounts(). Read(stores) answers every declared read for every store and returns them as result[read handle, store index]. Run(kernel, stores) applies a kernel to clones of the stores first and answers the reads from the results;
the inputs are not changed.
The boundary, measured: one call returns many reads, but each read makes its own transform by default. A transform is
shared between reads only inside an internal planner that is not exposed, and that planner was slower than the default on
the nested, duplicated reads it was measured on. Run is not fused: the kernel's pass over the cells and the read
transforms are separate passes.
using Phasor;
var a = new Pleroma(new HologramShape { FlagBits = 3 });
var b = new Pleroma(new HologramShape { FlagBits = 3 });
a.Add(new HologramItem(Key: 1, Flags: 0b101), 2);
b.Add(new HologramItem(Key: 1, Flags: 0b011), 5);
// Declare every read once; Read answers all of them for all stores in one call.
var reads = new PleromaReadSet();
int onBit0 = reads.AddFlagMarginal(new[] { 0 });
int onBits12 = reads.AddFlagMarginal(new[] { 1, 2 });
int total = reads.AddTotal();
int bitCounts = reads.AddBitCounts();
var result = reads.Read(new[] { a, b }); // result[read handle, store index]
System.Console.WriteLine($"a: bit 0 {string.Join(" ", result[onBit0, 0])} | total {result[total, 0][0]} | bit counts {string.Join(" ", result[bitCounts, 0])}");
System.Console.WriteLine($"b: bits 1,2 {string.Join(" ", result[onBits12, 1])} | total {result[total, 1][0]}");
// Run: a kernel flips bit 0 of every item in clones of the stores, then the same reads are answered.
var flip = new PleromaKernel(a.Shape);
flip.Add(flagMask: 0b001);
var ran = reads.Run(flip, new[] { a, b });
System.Console.WriteLine($"after flip, a: bit 0 {string.Join(" ", ran[onBit0, 0])}; input a unchanged: {string.Join(" ", result[onBit0, 0])}");
Output:
a: bit 0 0 2 | total 2 | bit counts 2 0 2
b: bits 1,2 0 5 0 0 | total 5
after flip, a: bit 0 2 0; input a unchanged: 0 2
A few flag words: ReadFlagWords
ReadFlagWords(words) returns the exact count of each listed word. For up to four words it sums the flag cells directly,
once per word; above four it uses the whole-store transform. Both paths return the same numbers. Every word must be below
2^FlagBits, otherwise an ArgumentOutOfRangeException is thrown and nothing is read.
Recorded at 14 flag bits (Release build, loaded machine, medians): about 134 microseconds for one word and about 90 more for each further word on the direct path, against 0.66 milliseconds for the whole-store transform. That also shows why the switch sits at four words. Use it for a handful of words; use Flags() when you want every word.
using Phasor;
var store = new Pleroma(new HologramShape { FlagBits = 6 });
store.Add(new HologramItem(Key: 1, Flags: 0b000101), 3);
store.Add(new HologramItem(Key: 2, Flags: 0b111000), 2);
store.Add(new HologramItem(Key: 3, Flags: 0b000101), 1);
// Exact counts of three flag words; the store is not transformed as a whole.
long[] few = store.ReadFlagWords(new uint[] { 0b000101, 0b111000, 0b000000 });
System.Console.WriteLine($"words: {string.Join(" ", few)}");
// Same values as indexing the whole-store read.
long[] all = store.Flags();
System.Console.WriteLine($"matches Flags(): {few[0] == all[0b000101] && few[1] == all[0b111000] && few[2] == all[0]}");
Output:
words: 4 2 0
matches Flags(): True
Widen the flag face: WidenFlags
WidenFlags(bits, at) returns a store with bits zero flag bits inserted at position at; bits at and above at move
up. The source store is not changed. The widening copies cells through a cached map: no item is decoded and none is added
again. The result equals a rebuild at the wider shape, and equals decoding the items and adding them back.
Cost, measured with the source map already cached for the layout (the first call for a layout builds it), Release build, loaded machine, medians: the native path grows with the number of cells, with a log-log slope of 0.96 from 10 to 14 flag bits
(4.1 microseconds at 10 bits, 17.3 at 12, 60.6 at 14). At 14 bits, decode and re-add took 1,593 microseconds, 26 times the
native path. Refused, with NotSupportedException: continuous components, relation faces, reserved keys. The result may
hold at most 16 flag bits.
using Phasor;
var narrow = new Pleroma(new HologramShape { FlagBits = 2 });
narrow.Add(new HologramItem(Key: 7, Flags: 0b10), 3);
narrow.Add(new HologramItem(Key: 8, Flags: 0b01), 1);
// One zero flag bit inserted at position 1: old bit 1 moves to bit 2, the new bit 1 is zero on every item.
Pleroma wide = narrow.WidenFlags(1, 1);
long[] counts = wide.Flags();
System.Console.WriteLine($"flag bits {wide.Shape.FlagBits}: word 100 -> {counts[0b100]}, word 001 -> {counts[0b001]}, word 010 -> {counts[0b010]}");
System.Console.WriteLine($"source store still {narrow.Shape.FlagBits} flag bits, total {narrow.Total()}");
Output:
flag bits 3: word 100 -> 3, word 001 -> 1, word 010 -> 0
source store still 2 flag bits, total 4
Many words in one pass: AddFlagWords
AddFlagWords(words, counts, key) adds counts[j] copies of words[j] at one key, in one pass over the cells. The result
is the same as one Add per word. The static overload Pleroma.AddFlagWords(stores, words, counts, key) applies one
batch to several stores of one shape; its counts are laid out store by store, word by word.
Recorded at 14 flag bits: one word about 80 microseconds, sixteen words about 215 to 237 microseconds.
using Phasor;
var batched = new Pleroma(new HologramShape { FlagBits = 4 });
var sequential = new Pleroma(new HologramShape { FlagBits = 4 });
uint[] words = { 0b0001, 0b0010, 0b1111 };
long[] counts = { 5, 2, 7 };
// One pass over the cells for all three words, at key 9.
batched.AddFlagWords(words, counts, key: 9);
// The same adds, one item at a time.
for (int j = 0; j < words.Length; j++)
sequential.Add(new HologramItem(Key: 9, Flags: words[j]), counts[j]);
System.Console.WriteLine($"total {batched.Total()}, word 0001 {batched.Flags()[0b0001]}, word 1111 {batched.Flags()[0b1111]}");
System.Console.WriteLine($"equal to {words.Length} sequential Adds: {batched.Equals(sequential)}");
Output:
total 14, word 0001 5, word 1111 7
equal to 3 sequential Adds: True
Tenants as flag bits
One store can hold several tenants. Give each tenant bits in the high end of the flag word and the data its own low bits.
Then the all-tenant total is Flags(data bits) on that one store: one transform, with the tenant bits summed out, whatever
the number of tenants. A tenant's own counts are its slice of Flags(tenant bits + data bits). That read transforms over
the tenant bits as well, so its transform grows with the tenant count; the all-tenant read does not. Both reads equal the
separate per-tenant stores cell for cell: 4,038 of 4,038 checks in the recorded run, which went up to 32 tenants with six
data bits.
The cap: tenant bits plus data bits must be at most 16, the flag-bit limit. As arithmetic, six data bits leave ten tenant bits (1,024 tenants in one store) and ten data bits leave six (64 tenants). The recorded run reached 32 tenants. Past the cap, split the tenants across stores.
using Phasor;
// Four tenants, two data bits each, and two tenant bits. The tenant id sits in the high bits of one store: FlagBits = 2 + 2 = 4.
const int dataBits = 2;
const int tenants = 4;
var shared = new Pleroma(new HologramShape { FlagBits = 2 + dataBits });
var separate = new Pleroma[tenants];
for (int t = 0; t < tenants; t++)
separate[t] = new Pleroma(new HologramShape { FlagBits = dataBits });
// Each write goes to the shared store (tenant in the high bits) and to that tenant's own store.
void Write(int tenant, uint data, long count)
{
shared.Add(new HologramItem(Key: 1, Flags: ((uint)tenant << dataBits) | data), count);
separate[tenant].Add(new HologramItem(Key: 1, Flags: data), count);
}
Write(0, 0b01, 2);
Write(1, 0b01, 3);
Write(1, 0b10, 1);
Write(3, 0b11, 4);
// All-tenant aggregate: the marginal on the data bits (the tenant bits are summed out, one transform).
long[] aggregate = shared.Flags(new[] { 0, 1 });
long[] sumOfSeparate = new long[1 << dataBits];
foreach (var s in separate)
for (int u = 0; u < sumOfSeparate.Length; u++)
sumOfSeparate[u] += s.Flags()[u];
System.Console.WriteLine($"aggregate {string.Join(" ", aggregate)}; equals the sum of tenant stores: {aggregate.SequenceEqual(sumOfSeparate)}");
// Per-tenant read: the marginal on tenant bits plus data bits, then the tenant's slice.
long[] perTenant = shared.Flags(new[] { 0, 1, 2, 3 });
for (int t = 0; t < tenants; t++)
{
long[] slice = perTenant.Skip(t << dataBits).Take(1 << dataBits).ToArray();
System.Console.WriteLine($"tenant {t}: {string.Join(" ", slice)}; equals its own store: {slice.SequenceEqual(separate[t].Flags())}");
}
Output:
aggregate 0 5 1 4; equals the sum of tenant stores: True
tenant 0: 0 2 0 0; equals its own store: True
tenant 1: 0 3 1 0; equals its own store: True
tenant 2: 0 0 0 0; equals its own store: True
tenant 3: 0 0 0 4; equals its own store: True
The kernel is the symmetry
Mix flag masks, left arrangements, ticks and key shifts in any weights, and one Apply runs the whole mix over every stored item in one pass. Eight of the 41 operators that change the store are kernel operators (key shifts on a keyed kernel). The other 33 cost one pass per term or are writes that no weighted sum can hold.
In plain words: a symmetry here is a move you can make to every item at once, such as flipping a flag, advancing the clock or reordering the arrangement. A kernel is a weighted mix of such moves, and the store applies the whole mix in one pass instead of one pass per move. This section says which moves can be mixed that way and which cannot, and the numbers at the end are how many of the store's 91 operations fall on each side.
The store holds the harmonic transform of its items on the product of four families: the key cycle Z_N (a cycle of N keys), the flag cube Z_2^m (m flag bits), the arrangements S_k (the orderings of k things) and the tick cycle Z_T (a cycle of T ticks). A harmonic is one component of that transform. Each stored item is one point of that product. A symmetry here is a translation of the product: add a key shift, XOR a flag mask, advance a tick, or multiply an arrangement. A weighted sum of translations is an element of the group algebra, and PleromaKernel holds exactly that element and applies it to every stored item in one pass. For a finite group, the linear maps that commute with its translations are exactly the group-algebra elements, so on the faces the kernel carries, the kernel is the general form of such a map.
The scope, by face:
- Flag and tick faces: every translation is an element of the kernel. Both are abelian, so their characters diagonalise every translation. A flag mask or a tick shift is one scalar on each harmonic block, and any weighted sum of them is one kernel. Bit permutations and sign flips of the flag word are not translations and cannot be added to a kernel.
- Arrangement face (S_2 to S_5 as built): every left multiplication is an element. A left arrangement element multiplies each irreducible block from the left, and weighted sums of those span every left multiplication. The right action, which relabels the other side of the block, and conjugation, which relabels both sides, cannot be added to a kernel.
- Key face: a key shift is an element on a keyed kernel.
PleromaKernel(shape, keyHarmonics: true)holds one factor per key harmonic, soAddKeyedputs a key shift in the same kernel as the flag, arrangement and tick elements, and oneApplyruns the mix.ShiftKeys(k)gives the same store as a one-element keyed kernel. A keyed kernel holds one number per stored cell. Key scaling,Hadamardproducts and per-key weights cannot be added to a kernel: each mixes key harmonics together, and no per-harmonic factor can hold that.
The measured boundary. The coverage record classifies the 91 operations that transform the store or read it by how they commute with the symmetry group. To commute with the group means that moving first and then applying the operation gives the same result as applying the operation first and then moving. A probe is a test that applies the operation to a store and to its moved copy, and compares the two. Some operations return a list, a count or a boolean, so each of those needs an output-action declaration first: a statement of how its output moves when its input is moved, which the probe needs before it can check the operation.
- 29 commute with every generator of the group. Seventeen of them join the kernel's axes. Eight are kernel operators (
Tick,Diffuse,Apply, the four kernel methods andAddKeyed), two are store sums (AddandSubtractof another store), and seven are reads of the flag face and the total, answered through a read set (Flags,Flags(bits),FlagsMany,FlagsUnion,Total, andPleromaReadSetReadandRun). The other twelve commute but lie off those axes:ShiftKeys(its element on a keyed kernel isAddKeyed),Hadamard,Syndrome,Project,TimeSpectrum, and the reads on the key, arrangement, tick, joint and pair axes. - 30 are linear or affine and break a named symmetry, for example a bit permutation of the flags, a key scaling, a Frobenius squaring, a bind or a write at a fixed flag word.
- 17 are not linear: choices, decodes, order statistics, draws and predicates, such as
Sample,MinKey,TimeMapandNearest. - 15 need an output-action declaration before a probe can classify them, among them
CombineCycles,Graded,Repairand the relation-face reads. The record gives the reason for each.
"Joins" in this section means: commutes with the group and lies on an axis the kernel carries (flag, left arrangement, tick, and the key harmonic of a keyed kernel). The 8 kernel operators are the ones counted in "8 of 41"; what you add to a kernel is an element.
Commuting with the group is necessary for kernel membership and not sufficient: on an unkeyed kernel ShiftKeys commutes and is not an element, and a keyed kernel is what makes it one. Sixty-seven of the 91 are checked by a probe run; the other 24 carry their reason.
RelationFace: Loop Holonomy in One Read
Purpose: store relations "b is offset ahead of a" between a small set of keys and read the sum of the
offsets around any loop without decoding a single relation. RelationFace(keys, modulus, tags, field) is a
complete face over the ordered key pairs (keys x keys cells per tag) in an exact field: cell (a, b) holds the
integer sum of the offsets stored as (a, b), and a count face holds how many relations sit there. A relation
keeps the orientation it was stored with; a loop that walks it from b to a reads its cell with weight -1.
Add(a, b, offset, tag),Remove,Count(a, b, tag),Pairs(tag);Add(other)andSubtract(other)merge and subtract cell by cell, so loop reads add and subtract with them. Tags keep several relation networks in one store (for example one per network id).RelationFace.FundamentalLoops(keys, edges)(orface.FundamentalLoops(tag)over the stored pairs) gives the fundamental loop basis of a BFS spanning forest: one loop per non-tree edge, E - V + 1 for a connected graph. A pair may appear once in the edge list.Read(loop, tag)is the one linear read, the integer loop sum lifted to (-Q/2, Q/2] (Q = the product of the field's primes; exact while the true sum stays within that range).Holonomy(loop, tag)is that sum mod n, andParity(loop, tag)its parity, the Z_2 read. Parity is the parity of the stored integers: it equals the holonomy mod 2 of integer offsets stored unreduced, not of offsets already reduced mod an odd n.- Measured (relation face, 16 keys, 40 to 60 relations, offsets in Z_257, q = 13,879 and 2^61 - 1): 2,028/2,028 fundamental-loop reads equal the true holonomy across 30 networks, 15 of them with 1 to 3 planted contradictions, and every loop of the 15 consistent networks reads 0; the Z_2 read equals the frustration parity and the integer holonomy mod 2 in 1,004/1,004 loops; subtracting two tagged stores of 30 networks gives the holonomy differences in 997/997 loops, and merging gives the sums in 997/997.
using Phasor;
var face = new RelationFace(keys: 4, modulus: 257);
face.Add(0, 1, 5); // key 1 is 5 ahead of key 0
face.Add(1, 2, 7);
face.Add(0, 2, 12); // consistent with the two above
face.Add(2, 3, 100);
face.Add(3, 0, 140); // 12 + 100 + 140 = 252, not 0 mod 257
foreach (var loop in face.FundamentalLoops())
System.Console.WriteLine($"edge {loop.NonTreeEdge}: holonomy {face.Holonomy(loop)}, parity {face.Parity(loop)}");
// edge 2: holonomy 0, parity 0
// edge 3: holonomy 252, parity 0
ChargeLattice: Charges From Loop Carries
Purpose: read integer charges from integer offsets on the edges of a periodic L x L x L lattice.
ChargeLattice(size, modulus) holds the lattice's oriented incidences. For an offset array (one per edge):
Circulation is each plaquette's oriented edge sum C; Reduce gives its representative r in (-n/2, n/2];
Carry is k = (C - r) / n, the whole turns the reduction dropped; Charges is each cube's outward-oriented
sum of its faces' carries. The charges are integers, total 0, depend only on the offsets mod n, and equal
ChargesFromReduced, which computes -(d3^T r) / n from the reduced values alone.
- Merging is not plain addition. Adding two offset arrays edge by edge gives
Charges(A + B) = Charges(A) + Charges(B) + Boundary(m), where m =
MergeCarry(A, B)is the carry the merge itself creates on each plaquette (two reduced values adding past half a turn). Charges add exactly only when m is 0 everywhere. - Measured (charge lattice, L = 4 and 5, n = 64): both routes agree and the total is 0 in 200/200 random configurations; charges are unchanged after adding multiples of n to random edges, 200/200; charges mod 2 equal the GF(2) boundary of the carry parities, 200/200; a planted +1/-1 pair is read exactly, 100/100. The merge law holds 300/300; plain additivity held in 46/100 merges of two planted stores and 0/100 merges of two random stores.
using System.Linq;
using Phasor;
var lat = new ChargeLattice(size: 4, modulus: 64);
var rng = new System.Random(7);
long[] a = Enumerable.Range(0, lat.EdgeCount).Select(_ => (long)rng.Next(64)).ToArray();
long[] b = Enumerable.Range(0, lat.EdgeCount).Select(_ => (long)rng.Next(64)).ToArray();
long[] qa = lat.Charges(a), qb = lat.Charges(b);
long[] merged = lat.Charges(a.Zip(b, (x, y) => x + y).ToArray());
long[] correction = lat.Boundary(lat.MergeCarry(a, b));
bool law = Enumerable.Range(0, lat.CubeCount).All(c => merged[c] == qa[c] + qb[c] + correction[c]);
System.Console.WriteLine($"{qa.Sum()} {qa.SequenceEqual(lat.ChargesFromReduced(a))} {law}"); // 0 True True
CycleHologram: The Keys-Only Case
Purpose: the Pleroma with keys only, kept as its own type for compatibility. A CycleHologram
stores a multiset of symbols with integer counts in a fixed-size array of finite-field power sums, and
gives the multiset back exactly: no rounding, no similarity threshold. It is the exact form of the phasor
bundle: a bundle (a sum of phasors) reads out approximately, with crosstalk that grows with load, and
CycleHologram is the same superposition on the discrete cycle of a finite field.
- Field: GF(p), default p = 2^61 - 1 with primitive root 37. A symbol is an integer in [0, p-2]; it
stands for the cycle element 37^symbol.
CycleHologram.Symbol(text)hashes text into that range (FNV-1a, so distinct strings collide with probability about n^2 / 2^62). - Storage:
2 * capacity + 1field elements (8 bytes each). The extra element is a check: it makes any set ofcapacity + 1distinct symbols refuse to decode rather than decode wrongly. - Algebra:
Add/Removea symbol with a count;Add(other)is multiset union,Subtract(other)is the exact signed difference (counts come back in (-p/2, p/2]). - Decode:
TryDecode(out items)returnstruewith every symbol and count when at mostcapacitydistinct symbols remain, andfalseotherwise. Items are sorted by symbol. - Custom fields:
new CycleHologram(capacity, prime, primitiveRoot)validates both (p - 1 is factored by trial division, so p must be below 2^40), or pass the distinct prime factors of p - 1 as a fourth argument for larger primes.Serialize/Deserializecarry the field with the data. - Refusal: with
capacity + 1distinct symbols the decode always refuses; withcapacity + 2or more, a wrong decode needs a coincidence of probability about 1/p. Every decode is re-encoded and must reproduce every stored element. - Reconciliation: the hologram is linear, so it can hold far more than
capacityitems and still decode once a subtraction leaves at mostcapacitydistinct symbols. Send one hologram, subtract the local one, decode the difference (see the quick start). - Measured (keys-only hologram): exact decode 260/260 across the sweeps (p = 65537 with root 3, and p = 2^61 - 1 with root 37; capacities 1, 8, 64; counts 1 to 5); over-capacity input refused 240/240 per field. Decoding 64 distinct symbols at p = 2^61 - 1 takes about 20 ms warm (about 26 ms cold) on one core of an i9-11950H.
using Phasor;
var apple = CycleHologram.Symbol("apple");
var pear = CycleHologram.Symbol("pear");
var names = new Dictionary<ulong, string> { [apple] = "apple", [pear] = "pear", [42] = "item 42" };
var stock = new CycleHologram(capacity: 8); // exact for up to 8 distinct symbols
stock.Add(apple, 3);
stock.Add(pear, 1);
stock.Add(42, 2); // integer symbols are exact, no hashing
var sold = new CycleHologram(capacity: 8);
sold.Add(apple, 1);
sold.Add(pear, 1);
stock.Subtract(sold); // exact signed multiset difference
if (stock.TryDecode(out var items))
foreach (var (symbol, count) in items)
System.Console.WriteLine($"{names[symbol]}: {count}"); // item 42: 2, then apple: 2 (sorted by symbol; pear is back to 0 and is absent)
Decoding works in four steps: Berlekamp-Massey finds the locator polynomial, Cantor-Zassenhaus splitting finds its roots, a Pohlig-Hellman discrete log turns each root into a symbol, and a Vandermonde solve recovers the counts. Prior art: this is the syndrome decoder of Reed-Solomon and BCH codes (Reed & Solomon 1960; Berlekamp 1968; Massey 1969), the construction behind the PinSketch set sketch and the minisketch library.
KeyedSummary: Exact Counts and Sums per Key
A keyed summary holds, for every dense key from 0 to Capacity - 1, an exact row count (Int64) and one exact signed sum per measure (Int128). It adds and removes rows, applies batches all-or-nothing, merges partial summaries, folds the keys through a key-to-group map in one pass, ranks the top keys by sum, count or mean, keeps pre-grouped totals exact as facts arrive and as keys change groups, and persists with CRC-32C checksums.
- Reads are O(1) per key. Memory is 8 + 16 x measures bytes per key (24 bytes with one measure). The TopK bounds add about 0.2 to 1.1 bytes per key more.
- A remove that would take a key below zero is refused with InvalidOperationException and changes nothing.
- Every count stays non-negative at every call boundary, so a count of zero means no rows.
- Fold: every key whose map value is g adds into group g; a map value of -1 leaves the key out.
- TopK: the k keys with the largest (or smallest) sum, count or mean of one measure, among keys with rows. Equal values are ordered by key ascending, or descending with keyDescending. The mean is compared exactly, as a fraction. Blocks of 256 and 4,096 keys keep max and min bounds that the summary refreshes itself after writes; a sum top-k reads only the blocks that can still reach the k-th best. Ties are visited, so the answer is exact. Count and mean bounds are loose when many keys tie, and then the scan is close to a full pass.
- FoldDelta (static) adds signed per-key deltas into group totals you hold, exactly and all-or-nothing. FoldRemap moves a key's current totals from one group to another, so a dimension row that changes groups moves its totals with it.
- Persistence: a flipped byte anywhere in
ToBytes()output is refused byFromByteswith InvalidDataException. The TopK bounds are not stored; the first TopK after a load rebuilds them. - Keys are dense codes (0 to Capacity - 1). Capacity comes from a memory budget you pass (
new KeyedSummary(capacity, measures, memoryBudgetBytes), default 1 GiB, about 44 million keys with one measure);KeyedSummary.CapacityFor(budget, measures)andBytesFor(capacity, measures)size it, and a capacity over the budget is refused before anything is allocated. Map 64-bit ids to codes in the caller. - Remove takes the measure totals of the rows removed; the summary does not remember rows, so a wrong total is not detected. Each call's total is one Int64 input: split a larger total into several calls (the sums stay exact).
Quick Start
using Phasor;
var sales = new KeyedSummary(capacity: 3, measures: 1);
sales.Add(key: 0, count: 2, measures: [150]);
sales.Add(key: 2, count: 1, measures: [40]);
sales.Remove(key: 0, count: 1, measures: [75]);
Span<Int128> sums = stackalloc Int128[1];
long rows = sales.Read(key: 0, sums); // rows = 1, sums[0] = 75
var segment = new[] { 7, -1, 7 }; // keys 0 and 2 join group 7; key 1 is unmatched
var counts = new long[8];
var groupSums = new Int128[8];
sales.Fold(segment, 8, counts, groupSums); // counts[7] = 2, groupSums[7] = 115
var topKeys = new int[2];
var topCounts = new long[2];
var topSums = new Int128[2];
int found = sales.TopK(KeyedRank.Sum, 0, descending: true, topKeys, topCounts, topSums); // found = 2, topKeys = [0, 2]
Int128 totals: AddWide and RemoveWide
AddWide and RemoveWide take measure totals as Int128, for rows whose total is past 64 bits. The sum is checked, so an
overflow past 128 bits throws instead of wrapping. The Int64 Add and Remove are unchanged.
using Phasor;
var wide = new KeyedSummary(capacity: 4, measures: 1);
Int128 half = Int128.MaxValue / 2;
wide.AddWide(key: 1, count: 1, measures: [half]);
wide.AddWide(key: 1, count: 1, measures: [half]); // a total of Int128.MaxValue - 1, exact
Span<Int128> sums = stackalloc Int128[1];
long rows = wide.Read(key: 1, sums); // rows = 2, sums[0] = Int128.MaxValue - 1
Pre-grouped totals: FoldDelta and FoldRemap
using Phasor;
var sales = new KeyedSummary(capacity: 3, measures: 1);
var map = new[] { 7, -1, 7 }; // key -> group (-1 leaves the key out)
var groupCounts = new long[8];
var groupSums = new Int128[8];
// Fact appends go into the summary and into the group totals in step.
sales.Add(key: 0, count: 1, measures: [75]);
sales.Add(key: 2, count: 1, measures: [40]);
KeyedSummary.FoldDelta(map, 8, 1, new[] { 0, 2 }, new[] { 1L, 1L }, new[] { 75L, 40L }, groupCounts, groupSums);
// groupCounts[7] = 2, groupSums[7] = 115
// Key 2 moves from group 7 to group 3: its current totals move with it.
map[2] = 3;
sales.FoldRemap(new[] { 2 }, new[] { 7 }, new[] { 3 }, groupCounts, groupSums);
// groupCounts[7] = 1, groupSums[7] = 75; groupCounts[3] = 1, groupSums[3] = 40
Crc32C
Crc32C.Compute(bytes) and Crc32C.Append(crc, bytes) give CRC-32C (Castagnoli), the checksum the keyed summary
writes. They use the SSE4.2 instruction on x64 and the CRC32C instruction on ARM64 where the CPU has one, and a byte
table otherwise; every path gives the same value. Crc32C.IsHardwareAccelerated reports which path runs. Measured on
one x64 machine shared with other work, with a 64 MB buffer and the median of 15 runs: about 9.3 GB/s on the hardware path against 0.54 GB/s on the table. The ARM64 path is
compiled but has not been run on ARM hardware.
Maturity: exactness is checked against a plain dictionary over millions of operations with removals, TopK and the group totals against naive oracles, with bounds mutated and watched red, and every corruption class is refused on load.
BitPlane: Row Bit-Planes
BitPlane is a static class for row masks. A row bit-plane is a ulong[] where bit i of word k is row 64k + i, so one
plane marks a set of rows. It combines planes with AND, OR, AND NOT and NOT, and counts set bits as exact long
values. Disjoint checks that two planes share no set bit. There is no state and no allocation, and nothing runs in
parallel: callers shard.
And,Or,AndNot(a, b, dest): element-wisedest = a & b,a | b,a & ~b.Not(a, rows, dest):dest = ~aover the firstrowsrows. Bits at or aboverowsare cleared, and words past the row count are written zero.PopCount(a, rows),PopCountAnd,PopCountOr,PopCountAndNot: the set bits among the firstrowsrows of the combined planes, with no result plane written.Disjoint(t, f): true whent & fhas no set bit. With a true plane and a false plane, this is the three-valued (Kleene) invariant: no row is both true and false.
Rules: bits at or above rows are ignored by every count. dest may be an exact alias of an operand (same start, same
length). A partial overlap with any operand throws ArgumentException. Operands of unequal length throw
ArgumentException, and rows beyond the words supplied throws ArgumentOutOfRangeException.
using Phasor;
// Rows 0..127 in two words: bit i of word k is row 64k + i.
ulong[] active = { 0b1011UL, 0xFFFFUL };
ulong[] flagged = { 0b0110UL, 0x00FFUL };
ulong[] both = new ulong[2];
BitPlane.And(active, flagged, both); // both = { 0b0010, 0x00FF }
long n = BitPlane.PopCountAnd(active, flagged, rows: 128); // 9, without writing 'both'
long m = BitPlane.PopCountAndNot(active, flagged, rows: 128); // 10
What the tests prove. BitPlaneTests (7 tests) checks every operation against a bit-by-bit boolean reference, at 11
row counts from 0 to 4,103, and checks the tail rule at every row count from 0 to 63 and from 192 to 255. It also checks
exact aliasing, the partial-overlap refusal, the argument checks, counts against materialised planes, and a
three-valued expression evaluated row by row on random planes. Five deliberate mutations of the source each failed a test.
Maturity. The loops are vectorised with Vector<ulong> and a scalar tail, on net8.0 and net10.0. No timing is
published for BitPlane yet, so this page makes no speed claim.
When not to use it. For one row, a plain bool read is simpler. BitPlane knows nothing about columns or tables;
it works on the words you give it.
Core Concepts
One Algebra, No Special Cases
A phasor face is a bundle of unit complex numbers (one per component). The single operation is per-component complex multiply (bind):
- exp(iaθ) · exp(ibθ) = exp(i(a+b)θ) → linear-phase components add values
- exp(iaθ) · exp(ibθ) = exp(i(a+b)θ) → log-phase components add logarithms (multiply values)
Unbind extracts one face from a composite by multiplying by the conjugate. Bundle superimposes faces (component-wise addition). Correlate reads the association strength (dot product).
Two Profiles
- PhasorCodec: Fixed 256-dim, interleaved (cos, sin), frozen identity prefix + learned orbital tail. For neural models.
- HoloCodec: Width-configurable, planar layout [reals | imags], SIMD-friendly, memoized number cache. For databases and similarity search.
- Hologram: Value type wrapping a phasor face, with operators for clarity and in-place statics for speed.
Numbers vs Symbols
Numbers: parsed as finite double, encoded with phase values on linear and log bands (bind = arithmetic). Symbols: treated as strings, encoded with deterministic random phasor signatures (invertible, bind is reversible).
PhasorLayout
Purpose: Layout constants defining the fixed face structure for PhasorCodec. All constants are public and compile-time known.
Constants
using Phasor;
System.Console.WriteLine(PhasorLayout.LinComps); // 16, linear-phase band for value addition
System.Console.WriteLine(PhasorLayout.LogComps); // 16, log-phase band for value multiplication
System.Console.WriteLine(PhasorLayout.IdComps); // 32 = Lin + Log, frozen identity (value + symbol signature)
System.Console.WriteLine(PhasorLayout.OrbitComps); // 96, learned orbital tail
System.Console.WriteLine(PhasorLayout.Comps); // 128 = Id + Orbit, total complex components
System.Console.WriteLine(PhasorLayout.Dim); // 256 = 2*Comps, total reals (interleaved cos/sin)
System.Console.WriteLine(PhasorLayout.FrozenReals); // 64 = 2*IdComps, frozen identity prefix (reals)
// Band offsets (component indices)
System.Console.WriteLine(PhasorLayout.LinStart); // 0
System.Console.WriteLine(PhasorLayout.LogStart); // 16
System.Console.WriteLine(PhasorLayout.OrbitStart); // 32
Example: Contract with AlgFormer
using Phasor;
// Pass to neural model for frozen/learned layout
int modelDim = PhasorLayout.Dim; // 256
int frozenPrefix = PhasorLayout.FrozenReals; // 64
System.Console.WriteLine($"Model dim: {modelDim}, frozen: {frozenPrefix}");
Encoding
Purpose: Convert strings (symbols or numbers) and numeric values into phasor faces.
PhasorCodec.IsNumber
Determine if a token is a finite number literal.
using Phasor;
bool is3 = PhasorCodec.IsNumber("3", out var v); // true, v = 3.0
bool isNeg = PhasorCodec.IsNumber("-2.5", out var v2); // true, v2 = -2.5
bool isSymbol = PhasorCodec.IsNumber("hello", out _); // false
bool isInfinity = PhasorCodec.IsNumber("Infinity", out _); // false
System.Console.WriteLine($"3 is number: {is3}, hello is number: {isSymbol}");
PhasorCodec.Encode(string)
Encode a symbol or number as a phasor face.
using Phasor;
// Number: phase on linear + log bands
double[] three_face = PhasorCodec.Encode("3");
System.Console.WriteLine($"Encoded '3': dim={three_face.Length}");
// Symbol: deterministic signature + orbital seed
double[] hello_face = PhasorCodec.Encode("hello");
System.Console.WriteLine($"Encoded 'hello': dim={hello_face.Length}");
PhasorCodec.Encode(string, int)
Encode at custom dimension (extends orbital tail; frozen identity unchanged).
using Phasor;
double[] face128 = PhasorCodec.Encode("hello", 128); // minimal (frozen 64 + orbital 64)
double[] face512 = PhasorCodec.Encode("hello", 512); // larger orbital tail
System.Console.WriteLine($"128-dim: {face128.Length}, 512-dim: {face512.Length}");
PhasorCodec.Encode(string, int, double): Log-Band Frequency Scale
Encode with optional log-band scaling for noise resilience (default 1.0). Use scale ≥ 1000 for new models requiring precise product decode; existing checkpoints must stay at scale 1.0.
using Phasor;
// Default scale (1.0), matches existing checkpoints
double[] face_default = PhasorCodec.Encode("5", 256, 1.0);
// High scale for new models, exact product decode under noise
double[] face_precise = PhasorCodec.Encode("5", 256, 1000.0);
System.Console.WriteLine("Encoded with scale 1.0 and 1000.0");
PhasorCodec.NumberFace(double)
Create a pure number face (identity comps filled, orbital = 0).
using Phasor;
// Minimal number representation (value only, no symbol signature)
double[] num = PhasorCodec.NumberFace(42);
System.Console.WriteLine($"Number face for 42: dim={num.Length}");
PhasorCodec.NumberFace(double, int)
Number face at custom dimension.
using Phasor;
double[] num128 = PhasorCodec.NumberFace(7, 128);
double[] num512 = PhasorCodec.NumberFace(7, 512);
System.Console.WriteLine($"128-dim: {num128.Length}, 512-dim: {num512.Length}");
PhasorCodec.NumberFace(double, int, double): Log-Band Scale
Number face with log-band scaling.
using Phasor;
// Precise log-band encoding
double[] face = PhasorCodec.NumberFace(1000, 256, 1000.0);
System.Console.WriteLine("Encoded number 1000 with log scale 1000.0");
The Algebra
Purpose: Compose and manipulate phasor faces using the one unified rule: per-component complex multiply (bind).
PhasorCodec.Bind(double[], double[])
Per-component complex multiply. On linear band: adds values. On log band: multiplies values.
using Phasor;
// Number arithmetic via binding
var three = PhasorCodec.NumberFace(3);
var two = PhasorCodec.NumberFace(2);
var sum = PhasorCodec.Bind(three, two); // 3 + 2 on linear band
System.Console.WriteLine("Bound three and two");
// Symbol composition
var role = PhasorCodec.Encode("role");
var agent = PhasorCodec.Encode("agent");
var role_binding = PhasorCodec.Bind(role, agent); // associate agent with role
System.Console.WriteLine("Bound role and agent");
PhasorCodec.Unbind(double[], double[])
Multiply by the conjugate. Extracts one component from a composite. Inverse of bind.
using Phasor;
var three = PhasorCodec.NumberFace(3);
var two = PhasorCodec.NumberFace(2);
var sum = PhasorCodec.Bind(three, two);
// Extract: sum / two = three (on linear band)
var recovered = PhasorCodec.Unbind(sum, two);
var corr = PhasorCodec.Correlate(recovered, three);
System.Console.WriteLine($"Recovery correlation: {corr:F2}"); // high value
PhasorCodec.Bundle(params double[][])
Superpose (sum) faces. Set-forming operation.
using Phasor;
var a = PhasorCodec.Encode("apple");
var b = PhasorCodec.Encode("banana");
var c = PhasorCodec.Encode("cherry");
// Bundle into a superposition
var set = PhasorCodec.Bundle(a, b, c);
System.Console.WriteLine("Bundled three symbols");
// Extract any one via unbind
var recovered_a = PhasorCodec.Unbind(set, b); // remove b
var recovered_a2 = PhasorCodec.Unbind(recovered_a, c); // remove c → a
System.Console.WriteLine("Extracted first symbol");
PhasorCodec.BindInto(double[], double[], double[])
In-place bind: accumulate into pre-allocated buffer. Hot-path operation for training loops.
using Phasor;
var a = PhasorCodec.Encode("x");
var b = PhasorCodec.Encode("y");
var acc = new double[PhasorLayout.Dim];
// No allocation: modifies acc in place
PhasorCodec.BindInto(acc, a, b);
PhasorCodec.BindInto(acc, b, a); // accumulate more binds
System.Console.WriteLine("In-place bind accumulated");
PhasorCodec.BundleInto(double[], double[])
In-place bundle: accumulate addition into buffer.
using Phasor;
var a = PhasorCodec.Encode("item1");
var b = PhasorCodec.Encode("item2");
var acc = new double[PhasorLayout.Dim];
PhasorCodec.BundleInto(acc, a);
PhasorCodec.BundleInto(acc, b);
System.Console.WriteLine("In-place bundle accumulated");
PhasorCodec.UnbindInto(double[], double[], double[])
In-place unbind: accumulate by conjugate into buffer.
using Phasor;
var composite = PhasorCodec.Encode("story");
var character = PhasorCodec.Encode("alice");
var acc = new double[PhasorLayout.Dim];
PhasorCodec.UnbindInto(acc, composite, character);
System.Console.WriteLine("In-place unbind accumulated");
PhasorCodec.Correlate(double[], double[])
Full-face correlation (dot product). Reads association strength and similarity.
using Phasor;
var x = PhasorCodec.Encode("sunrise");
var y = PhasorCodec.Encode("sunrise");
double same = PhasorCodec.Correlate(x, y); // high (same symbol)
System.Console.WriteLine($"Self-correlation: {same:F2}");
double different = PhasorCodec.Correlate(x, PhasorCodec.Encode("sunset")); // low
System.Console.WriteLine($"Different correlation: {different:F2}");
Decoding
Purpose: Read numeric and symbolic values from phasor faces.
PhasorCodec.DecodeSum(double[], int)
Decode the value encoded on the linear-phase band (sum semantics). Returns argmax over range [0, max].
using Phasor;
// Create and bind two numbers
var five = PhasorCodec.NumberFace(5);
var three = PhasorCodec.NumberFace(3);
var sum = PhasorCodec.Bind(five, three);
// Decode the result
int result = PhasorCodec.DecodeSum(sum, 20); // searches [0, 20]
System.Console.WriteLine($"Decoded sum: {result}"); // 8
PhasorCodec.DecodeProduct(double[], int)
Decode the value on the log-phase band (product semantics). Returns argmax over range [1, max] (0 always yields 0 log).
using Phasor;
// Product: bind multiplies on log band
var four = PhasorCodec.NumberFace(4);
var three = PhasorCodec.NumberFace(3);
var product = PhasorCodec.Bind(four, three);
// Decode
int result = PhasorCodec.DecodeProduct(product, 20);
System.Console.WriteLine($"Decoded product: {result}"); // 12
PhasorCodec.DecodeProduct(double[], int, double): Log-Band Scale
Decode with log-band frequency scale. Must match the scale used in Encode.
using Phasor;
// Encode with high scale for new models
var four = PhasorCodec.NumberFace(4, 256, 1000.0);
var three = PhasorCodec.NumberFace(3, 256, 1000.0);
var product = PhasorCodec.Bind(four, three);
// Decode with matching scale
int result = PhasorCodec.DecodeProduct(product, 20, 1000.0);
System.Console.WriteLine($"Decoded precise product: {result}"); // 12
Advanced Cleanup
Purpose: Recover multiple factors from superposed/composite faces using iterative cleanup (resonator networks).
PhasorCodec.ResonatorNetworkDecode
Iteratively extract multiple simultaneously-bound factors from a composite face.
using Phasor;
// Create candidates
var one = PhasorCodec.Encode("one");
var two = PhasorCodec.Encode("two");
var three = PhasorCodec.Encode("three");
// Bind them together (multiple role-value pairs)
var composite = PhasorCodec.Bind(one, two);
composite = PhasorCodec.Bind(composite, three);
// Recover factors iteratively
var candidates = new[] { one, two, three };
var recovered = PhasorCodec.ResonatorNetworkDecode(composite, candidates, maxIterations: 10, convergenceThreshold: 1e-6);
foreach (var (idx, corr) in recovered)
{
System.Console.WriteLine($"Recovered index {idx} with correlation {corr:F2}");
}
PhasorCodec.CleanupNearest
Find the best-matching candidate (single nearest projection).
using Phasor;
// Noisy face (result of operations, may be corrupted)
var target = PhasorCodec.Encode("elephant");
// Candidates to search
var candidates = new[]
{
PhasorCodec.Encode("elephant"),
PhasorCodec.Encode("zebra"),
PhasorCodec.Encode("giraffe")
};
// Find nearest
var (bestIdx, correlation) = PhasorCodec.CleanupNearest(target, candidates);
System.Console.WriteLine($"Nearest candidate: index {bestIdx}, correlation {correlation:F2}");
Hologram Type
Purpose: Value type wrapping a phasor face, enabling operators for readability and in-place statics for performance.
Creating Holograms
using Phasor;
// From a value
var five = Hologram.FromNumber(5);
var three = Hologram.FromNumber(3);
// From a symbol
var hello = Hologram.FromSymbol("hello");
// From a raw array
double[] raw = PhasorCodec.NumberFace(7);
Hologram holo = new Hologram(raw);
// Zero hologram
Hologram zero = Hologram.Zero(PhasorLayout.Dim);
Properties
using Phasor;
var h = Hologram.FromNumber(42);
System.Console.WriteLine($"Dim: {h.Dim}"); // 256
System.Console.WriteLine($"Comps: {h.Comps}"); // 128
System.Console.WriteLine($"Reals.Length: {h.Reals.Length}"); // 256
Implicit Conversions (Free, No Copy)
using Phasor;
// Hologram to double[]
var h = Hologram.FromNumber(5);
double[] raw = h; // implicit, no copy
// double[] to Hologram
double[] array = PhasorCodec.Encode("hello");
Hologram h2 = array; // implicit, no copy
Operators (Expressive Style, Allocating)
These are for prototyping and clarity, not hot loops. Each allocates a new array.
Bundle (+)
using Phasor;
var x = Hologram.FromSymbol("x");
var y = Hologram.FromSymbol("y");
var pair = x + y; // superposition
System.Console.WriteLine($"Bundled: {pair}");
Bind (*)
using Phasor;
var color = Hologram.FromSymbol("red");
var object_ = Hologram.FromSymbol("apple");
var red_apple = color * object_; // bind: associate
System.Console.WriteLine($"Bound: {red_apple}");
Unbind (/)
using Phasor;
var three = Hologram.FromNumber(3);
var two = Hologram.FromNumber(2);
var sum = three * two;
var recovered = sum / two; // extract three
System.Console.WriteLine($"Unbound: {recovered}");
Conjugate (~)
using Phasor;
var h = Hologram.FromNumber(7);
var inv = ~h; // per-component conjugate
System.Console.WriteLine($"Conjugate: {inv}");
Correlate (Instance Method)
Read association strength (dot product).
using Phasor;
var a = Hologram.FromSymbol("theory");
var b = Hologram.FromSymbol("theory");
double corr = a.Correlate(b); // high (same symbol)
System.Console.WriteLine($"Correlation: {corr:F2}");
In-Place Hot-Path Statics (No Allocation)
These are the supported operations for training loops and tight accumulation.
BindInto
using Phasor;
var a = Hologram.FromSymbol("factor1");
var b = Hologram.FromSymbol("factor2");
var acc = new double[PhasorLayout.Dim];
// Accumulate binds without allocation
Hologram.BindInto(acc, a.Reals, b.Reals);
Hologram.BindInto(acc, b.Reals, a.Reals);
System.Console.WriteLine("In-place bind accumulated");
BundleInto
using Phasor;
var items = new[]
{
Hologram.FromSymbol("item1"),
Hologram.FromSymbol("item2"),
Hologram.FromSymbol("item3")
};
var acc = new double[PhasorLayout.Dim];
foreach (var item in items)
{
Hologram.BundleInto(acc, item.Reals); // accumulate superposition
}
System.Console.WriteLine("In-place bundle accumulated");
UnbindInto
using Phasor;
var composite = Hologram.FromSymbol("story");
var character = Hologram.FromSymbol("alice");
var acc = new double[PhasorLayout.Dim];
Hologram.UnbindInto(acc, composite.Reals, character.Reals);
System.Console.WriteLine("In-place unbind accumulated");
ToString
using Phasor;
var h = Hologram.FromNumber(42);
System.Console.WriteLine(h.ToString()); // "Hologram(dim=256)"
HoloCodec
Purpose: SIMD-friendly codec for databases and similarity search. Planar layout [reals | imags], width-configurable, memoized number cache.
Creating an Instance
using Phasor;
// Default: dim=512, numberBand=25% of comps
var codec = new HoloCodec();
System.Console.WriteLine($"Codec dim: {codec.Dim}");
// Custom dimension and number band
var small = new HoloCodec(dim: 256, numberBand: 0.5);
System.Console.WriteLine($"Small codec dim: {small.Dim}");
Number(double)
Create a number face (value on linear band, rest 0).
using Phasor;
var codec = new HoloCodec();
double[] num = codec.Number(42);
System.Console.WriteLine($"Number face dim: {num.Length}");
Symbol(string)
Create a symbol face (deterministic signature, all comps filled).
using Phasor;
var codec = new HoloCodec();
double[] sym = codec.Symbol("keyword");
System.Console.WriteLine($"Symbol face dim: {sym.Length}");
Encode(string)
Encode a symbol or number (dispatches via IsNumber).
using Phasor;
var codec = new HoloCodec();
double[] num_face = codec.Encode("42"); // parses as number
double[] sym_face = codec.Encode("hello"); // symbol
System.Console.WriteLine("Encoded");
Bind(double[], double[]): Static
Per-component complex multiply (SIMD-accelerated, planar layout).
using Phasor;
var codec = new HoloCodec();
var a = codec.Number(5);
var b = codec.Number(3);
var product = HoloCodec.Bind(a, b);
System.Console.WriteLine($"Bound: dim={product.Length}");
Unbind(double[], double[]): Static
Extract one component (SIMD).
using Phasor;
var codec = new HoloCodec();
var a = codec.Number(5);
var b = codec.Number(3);
var product = HoloCodec.Bind(a, b);
var recovered = HoloCodec.Unbind(product, b);
System.Console.WriteLine("Unbound");
AddInto(double[], double[]): Static
In-place addition (SIMD).
using Phasor;
var codec = new HoloCodec();
var acc = new double[codec.Dim];
var x = codec.Symbol("item");
HoloCodec.AddInto(acc, x);
System.Console.WriteLine("Added in-place");
SubInto(double[], double[]): Static
In-place subtraction (exact removal for update/delete, SIMD).
using Phasor;
var codec = new HoloCodec();
var acc = codec.Symbol("collection");
var remove = codec.Symbol("item");
HoloCodec.SubInto(acc, remove);
System.Console.WriteLine("Subtracted in-place");
Sub(double[], double[]): Static
Subtract, return new array (SIMD).
using Phasor;
var codec = new HoloCodec();
var a = codec.Symbol("state");
var b = codec.Symbol("delta");
var result = HoloCodec.Sub(a, b);
System.Console.WriteLine("Subtracted");
Bundle(params double[][]): Static
Superpose (sum) faces (SIMD).
using Phasor;
var codec = new HoloCodec();
var a = codec.Symbol("red");
var b = codec.Symbol("green");
var c = codec.Symbol("blue");
var rgb = HoloCodec.Bundle(a, b, c);
System.Console.WriteLine("Bundled");
Dot(double[], double[]): Static
SIMD dot product (similarity search kernel).
using Phasor;
var codec = new HoloCodec();
var query = codec.Symbol("search");
var candidate = codec.Symbol("search");
double similarity = HoloCodec.Dot(query, candidate);
System.Console.WriteLine($"Similarity: {similarity:F2}");
DecodeNumber(double[], int)
Decode noisy number face to nearest integer in [0, max].
using Phasor;
var codec = new HoloCodec();
// Encode, bind, add noise (simulated by operations)
var num = codec.Number(42);
var noisy = HoloCodec.Bind(num, codec.Symbol("noise_factor"));
// Recover
int recovered = codec.DecodeNumber(noisy, 100);
System.Console.WriteLine($"Recovered: {recovered}");
Gotchas & Invariants
One Algebra, One Operation
Bind/unbind/bundle/correlate are the only operations. There is no second "calculator" route. Numbers are special only at encode time (which comps get phase); at compose and readout they obey the same rules as symbols.
Two Layouts, Do Not Mix
- PhasorCodec & Hologram use interleaved (cos, sin, cos, sin, ...)
- HoloCodec uses planar ([reals | imags])
A face from one is garbage to the other. Do not mix them.
Dimension Floor
- PhasorCodec: dim >= 128 (64 reals frozen + >= 64 orbital). Odd dims align down silently.
- HoloCodec: dim >= 16. Constructor throws
ArgumentExceptionbelow 8 comps.
Non-Finite Guard
Both PhasorCodec.NumberFace and HoloCodec.Number throw ArgumentException on NaN/Infinity, fail-fast, so a face can never be silently poisoned with a NaN phase. The decode cache (DecodeSum/DecodeProduct) only ever passes finite integers, so the guard never fires on the hot decode path.
Hologram Operators Allocate; In-Place Statics Don't
Every + / * / / / ~ on Hologram allocates a new array. Measured ~1.7x overhead at d=64/96.
For tight loops, use:
Hologram.BindInto(acc, a.Reals, b.Reals): no allocationHologram.BundleInto(acc, x.Reals): no allocationHologram.UnbindInto(acc, a.Reals, b.Reals): no allocation
Or use PhasorCodec directly with raw double[].
Hologram.Reals is a Live Reference
Modifying Hologram.Reals mutates the hologram. Use it read-only or pass to in-place methods.
Frozen Identity + Mutable Orbital Tail
PhasorCodec's frozen 64-reals prefix (identity comps) never changes across operations. Learning lives in the orbital tail (orbital comps). Phasor itself has no mutable state, only encode and compose operations.
Memoized Decode Cache (Unbounded Growth)
PhasorCodec and HoloCodec cache NumberFace(int) results to amortize trig computation. Growth is bounded by the candidate set size (max parameter to DecodeSum/DecodeProduct), typically small. The caches are thread-safe (ConcurrentDictionary).
Log-Band Scale Must Match
If faces are encoded with logFreqScale=1000.0, they must be decoded with the same scale. Mismatch breaks recovery.
New models: use scale ≥ 1000. Existing checkpoints: must stay at scale=1.0 (default) because frozen faces were encoded at that scale.
Deterministic Hashing Only
Symbol encoding uses FNV-1a with fixed salts (0xC0DEC0DE, 0xF00DBABE, 0x9E3779B9, 0x85EBCA77). Reproducible, not cryptographic. Changing a salt silently re-encodes vocabulary.
HoloCodec Contract with Frozen Identity
HoloCodec's number band widens with dim. Default numberBand=0.25 means 25% of comps are allocated to value (linear band); the rest carry no numeric signature.
Scalar (Memory-Bound) vs SIMD
- PhasorCodec is scalar (no SIMD). Vector attempt was ~28% slower at d=64 (memory-bound, not compute-bound).
- HoloCodec uses
Vector<double>SIMD for planar layout (all reals, then imags).
For scale, use HoloCodec.
License
Proprietary — this package ships the compiled library only; see LICENSE for the full terms. Installing and using unmodified copies for internal evaluation and non-commercial development or testing is licensed; commercial, production, or revenue-generating use requires a separate commercial licence from the owner. No feature on this page is gated behind a tier or a licence key — every capability described works identically with or without one. Copyright (c) 2026 Dongyang Stephen Chen, trading as Evaluated Applications. All rights reserved.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (5)
Showing the top 5 NuGet packages that depend on EvaluatedApplications.Phasor:
| Package | Downloads |
|---|---|
|
EvaluatedApplications.HoloDb
An embeddable SQL database for .NET that builds its own fast access paths from the queries it runs, so there is no index to design. Tables are typed columns, held in memory or on disk, with ACID transactions over a write-ahead log, larger-than-memory paged storage whose every page is checksummed, and exact aggregates: integer sums are 128-bit and throw instead of wrapping. It is for applications that need dashboards, change feeds and analytics over their own data without running a database server, and it includes similarity search (NEAREST), NULL support, ClickBench-compatible analytic SQL, atomic change batches, and an EvalApp extension with async execution. |
|
|
EvaluatedApplications.AlgFormer
The reusable phasor-model engine: define a model, train it data-parallel through an EvalApp pipeline, and run inference (predict / generate, with an optional KV cache). Ships two param-for-param comparable cores — AlgFormer (softmax dot-product attention) and HoloFormer (holographic-native attention: bind the key to its value, causally bundle into one superposed memory trace, then resonate the query to retrieve, O(T·d) with O(1) KV-serve) — plus HoloShape capacity-law sizing, compute-equivalent (invisible-param) accounting, a built-in subword tokenizer, checkpoint save/load with in-place growth (context/shifts/vocabulary), and MQTT-based distributed/swarm training. HoloFormer also ships an opt-in, default-OFF per-layer Adam gate ("ADAM & EVE") that nudges a layer's update toward a codebook-derived target norm range; default off and byte-identical when unused, but live in production via the GPU-fused trainer (see AlgFormer.Gpu) since 2026-09-24. Pure double[] + SIMD, no external ML runtime. Built on the Phasor VSA codec and the EvalApp pipeline runtime. |
|
|
EvaluatedApplications.HoloVoxel
Beta: An embeddable holographic voxel/spatial engine for .NET. A chunk of the world is stored as ONE Phasor superposition (position bound to content), not an array of voxels — and level-of-detail is the readout itself: decode with fewer components and the interference blend goes fuzzy, so "crisp near, dreamy far" falls out of the math with no discrete LOD meshes and no popping. Decode a chunk as a colour field, a triangulated surface mesh with analytic per-vertex normals, or an argmax block; per-point decode confidence drives an adaptive component budget that spends extra detail only where the world is genuinely ambiguous, and far (step>1) reads spatially average height and material instead of point-sampling, so distant terrain reads as smooth shapes rather than noise. Entities (creatures/props) store and decode the same holographic way via HoloModel, with two ready-made examples (Creatures.Wraith/Crystal). Ships a built-in procedural world generator (WorldGen: wave-based biomes, an Arena/RaceMode/ringworld-periodic terrain, a connected maze of wall structures) and a flat-to-cylinder Ring warp for ringworld render spaces, or implement IWorldSource to drive the engine from your own world data entirely. Truncated holograms make memory scale with perceived detail rather than world size; fused SIMD bind/correlate keep generation and decode allocation-free. Rendering-agnostic (verified headless) and content-agnostic — supply your own block palette. Built on the Phasor VSA codec. |
|
|
EvaluatedApplications.HoloGenome
Encode DNA as phasor holograms whose geometry comes from measured DNA physics. Each base is a Phasor symbol face carried on a helical phase that advances by published, sequence-dependent dinucleotide twist (Olson et al. 1998, built in), optionally with a local DNA-shape layer (minor groove width, propeller twist, roll, helix twist) read from a pentamer table you supply. Windows of sequence become fixed-width vectors you can store in HoloDb, compare, or feed to any regression. Also grades sequences against your own measurements, with no files or console so it runs in a browser: a zero-parameter helical score, a cross-validated ridge readout on the holograms, and a fitted phasor model that reports the helical period (bases per turn), each with a correlation and a block-resampled 95% interval. Research and education only: it makes no health, disease-risk or drug-response claims. Experimental (0.x). |
|
|
EvaluatedApplications.PleromaMind
A persistent conversational unit built on exact holograms, created empty from a design you choose and taught from your own text; the package ships no voice and no text. Its alphabet is Prism's 320 tokens (characters, common words and chat tags) plus subwords you can mint from your own text. It learns every exchange it hears and says, plus anything taught or ingested by reference, into exact per-conversation counts for the context lengths you pick. Memory grows with the text learned (ModelDesign.Estimate gives the cost by formula). It has a conversation memory for long matches (its trust can be calibrated on your own text and is journaled) and an optional learned sharing layer (token classes and solved trust) that learns only when you call LearnStep, with Compare scoring held-out text with and without it; it offers N sampled replies to pick from without learning anything; takes bounded and fading approve or disapprove feedback without touching memory; forgets a conversation exactly; and rebuilds its whole state bit for bit from its journal on a HoloDb page store, including after a crash. Every choice can be inspected per context length. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.6.0 | 83 | 11/11/2026 |
| 3.5.0 | 81 | 11/11/2026 |
| 3.4.0 | 74 | 11/10/2026 |
| 3.3.0 | 77 | 11/10/2026 |
| 3.2.0 | 78 | 11/10/2026 |
| 3.1.1 | 74 | 11/10/2026 |
| 3.1.0 | 71 | 11/10/2026 |
| 3.0.0 | 228 | 11/9/2026 |
| 2.23.0 | 141 | 11/3/2026 |
| 2.22.0 | 140 | 10/24/2026 |
| 2.21.1 | 123 | 10/23/2026 |
| 2.21.0 | 110 | 10/23/2026 |
| 2.20.0 | 121 | 10/19/2026 |
| 2.19.1 | 176 | 10/17/2026 |
| 2.19.0 | 103 | 10/17/2026 |
| 2.18.0 | 126 | 10/16/2026 |
| 2.17.0 | 210 | 10/12/2026 |
| 2.16.0 | 141 | 10/11/2026 |
| 2.15.0 | 104 | 10/11/2026 |
| 2.14.0 | 146 | 10/10/2026 |