florecon
Two dual conservation laws over accounting data. The same ledger is read two ways, and florecon is the algebra for each:
- reconcile (
strategy) — parse a bag of entries into a partition of groups. Conserves identity: every input lands in exactly one group or in residual, nothing lost, nothing invented. - allocate (
alloc) — re-express a coarse cost on a finer basis. Conserves value: a cost split across a driver is exact to the penny, residuals parked in-cube rather than vanishing.
They are duals — partition by identity vs. couple by value — sharing one stance: no privileged numeraire. Every number is a closure over the caller's own payload, so multi-currency is just different closures.
Reconcile
Given a bag of entries (each a stable id plus an opaque payload), parse it into a list of groups.
use *;
let strategy = seq;
The model
Entry<E> { id, data }— a stableIdand an opaque payload. Derefs to the payload, so|e| e.amountreaches a field whilee.idnames identity.Group { members: Vec<Id>, origin, reason }— a set of whole entries.Strategy<E>: bag -> (groups, residual)— a pure function. The output is a partition: every input id lands in exactly one group or in residual.
A match is judged by an acceptance closure over a GroupView
(|g| g.net(|e| e.amount).abs() <= 5 * g.min_leg(|e| e.amount) / 10_000) — no
tolerance type, the author writes the inequality and picks the lane. Conservation
is identity, not arithmetic.
combinators seq partition_by when windowed fixed_point restart accept_if explain identity soak
leaves exact_1to1 agg_net signal_group cumulative subset_sum flow(FlowSpec)
flow uses min-cost flow internally (the netsimplex crate) to
discover the optimal matching, but reads it back as whole-row
connected-component clusters — a row is never split. The break, if any, is the
cluster's net, which a downstream accept_if can gate.
Allocate
A Measure is a sparse quantity over named axes. Re-express a coarse cost on a
finer basis, exact to the penny.
use *;
let rent = build;
let rev = build;
let a = rent.allocate;
assert_eq!; // conserves value
assert_eq!; // geog 2 parked on ANY, in-cube
The model
- reshape —
rekeyrewrites each cell's key and re-sums (subsumes marginalize / roll-up / relabel). - couple —
combinealigns shared axes and broadcasts the rest, the scalar op a closure (|a, b| a + b). - down-project —
allocatesplits a coarse cost across a driver, exact to the penny. - select / route —
select/partition/slicequery a cube;group_byroutes a per-group rule and recombines.
The one idea is ANY: a reserved coord meaning "unresolved on this axis".
Every residual — a missing driver, an undriven dimension, sub-threshold dust — is
mass parked on ANY, in-cube. Two inverse moves shuttle mass along the grain:
rake refines ANY → real detail, vacuum surrenders detail → ANY. There is
no type-level conserved/unconserved distinction — conservation is a property you
assert (a.total()), not one the compiler enforces. The cyclic (reciprocal)
case reaches for fixed_point, as recon does.
Workspace
florecon— the reconcile + allocate algebras (this crate).netsimplex— the domain-agnostic min-cost transportation solver behindflow.