runtime: transduction decomposes a signal, it does not convert it
#144 moved transduction into the language and got the dispatch right. It got the result type wrong: transduce(signal, modality) -> Geometry yields one vector per signal, and one vector is a fingerprint. A fingerprint can be matched and ranked; that is all. It cannot be decomposed, cannot have one part grounded while another is not, and cannot be contradicted in one part while holding in another, because it has no parts. A song is not a point. It decomposes into pitch, interval, rhythm, harmonic function -- components, each with its own geometry, plus the relations among them. The song IS the structure of the relations. transduce now returns a Manifold: named components carrying geometry, and typed weighted relations between them. Signal in, subgraph out. Components are addressed by key, never by index, because the key is what survives persistence -- a component becomes a node and is separately groundable precisely because it is separately named. Relation weight IS the grounding (correspondence-and-censorship.md 1), so a realizer's relations arrive already grounded and there is no score computed beside them.
This commit is contained in:
+335
-21
@@ -6290,22 +6290,319 @@ el_val_t geometry_to_f32le_hex(el_val_t g) {
|
||||
return (el_val_t)(uintptr_t)out;
|
||||
}
|
||||
|
||||
|
||||
/* ── Manifold: a transduced signal is a SUBGRAPH, not a point ────────────────
|
||||
*
|
||||
* WHAT THIS CORRECTS. #144 gave transduction a home in the language and got
|
||||
* the DISPATCH right — realizers declared in El, resolved by name, no runtime
|
||||
* patch per modality. It got the OUTPUT TYPE wrong.
|
||||
* `transduce(signal, modality) -> Geometry` yields one vector per signal, and
|
||||
* one vector is a FINGERPRINT. A fingerprint can be matched and it can be
|
||||
* ranked; that is the whole of what it can ever do. It cannot be decomposed,
|
||||
* cannot be partially grounded, and cannot be contradicted in one part while
|
||||
* holding in another — because it has no parts.
|
||||
*
|
||||
* A song is not a point. It decomposes into pitch, interval, rhythm, harmonic
|
||||
* function, phrase structure: components, each with its own geometry, plus the
|
||||
* relations between them. THE SONG IS THE STRUCTURE OF THE RELATIONS. A
|
||||
* transducer that returns a single vector has not transduced the song, it has
|
||||
* summarised it — and the summary discards precisely the thing that made the
|
||||
* song reasonable-about.
|
||||
*
|
||||
* So transduction produces a MANIFOLD: named components, each carrying its own
|
||||
* geometry, and typed weighted relations among them. Signal in, subgraph out.
|
||||
* Conversion was never the operation.
|
||||
*
|
||||
* COMPONENTS ARE ADDRESSED BY KEY, NEVER BY INDEX. The key is what survives
|
||||
* persistence: a component becomes a node, and that node is separately
|
||||
* groundable precisely because it is separately NAMED. Index-addressing would
|
||||
* make a grounding reference positional, and a positional reference into a
|
||||
* decomposition whose arity can change is not a reference at all. Duplicate
|
||||
* keys are refused for the same reason: two components answering to one name
|
||||
* is not an addressing scheme.
|
||||
*
|
||||
* RELATION WEIGHT IS THE GROUNDING — there is no second field and no score to
|
||||
* compute. Per correspondence-and-censorship.md §1, grounding is an attribute
|
||||
* of the edge and it IS the hebbian weight; a grounding subsystem is a
|
||||
* supervisor invented for something that should be a property of the
|
||||
* substrate. A relation emitted by a realizer therefore arrives with its
|
||||
* grounding already on it and moves thereafter by use and by decay (§4: change
|
||||
* is not a consequence of use, it is use). Nothing in here computes a
|
||||
* grounding, and nothing observes one.
|
||||
*
|
||||
* A relation naming an endpoint that does not exist is REFUSED, not dropped. A
|
||||
* decomposition that silently loses edges is indistinguishable from one that
|
||||
* never had them — the same class of defect #141 exists to end.
|
||||
*
|
||||
* OWNERSHIP mirrors Geometry exactly. A Manifold is owned by the El caller and
|
||||
* released with manifold_free. manifold_add COPIES the geometry handed to it,
|
||||
* so a caller may free its own vector immediately and no component's geometry
|
||||
* is ever aliased. Keys, roles and relation strings are _persist copies, NOT
|
||||
* arena copies: a Manifold outlives the request arena that built it (a
|
||||
* realizer can be invoked from inside a handler), so an arena-tracked key
|
||||
* would dangle at el_request_end. manifold_free owns their release.
|
||||
*/
|
||||
|
||||
#define EL_MAGIC_MFLD 0xE1608E02u
|
||||
|
||||
typedef struct {
|
||||
char* key; /* addressable name, unique within the manifold */
|
||||
char* role; /* what KIND of component this is, realizer's vocabulary */
|
||||
ElGeometry* g; /* owned copy; never aliases the caller's value */
|
||||
} ElComponent;
|
||||
|
||||
typedef struct {
|
||||
char* from; /* component key */
|
||||
char* rel; /* relation name */
|
||||
char* to; /* component key */
|
||||
double weight; /* the grounding; §1 — one quantity, not two fields */
|
||||
} ElRelation;
|
||||
|
||||
typedef struct {
|
||||
ElHeader hdr;
|
||||
ElComponent* comps;
|
||||
size_t ncomp, capcomp;
|
||||
ElRelation* rels;
|
||||
size_t nrel, caprel;
|
||||
} ElManifold;
|
||||
|
||||
/* Resolve an el_val_t to a live Manifold, or NULL. Every accessor goes through
|
||||
* this, so a stale/foreign/zero value is a clean 0-return, never a deref. */
|
||||
static ElManifold* mfld_of(el_val_t m) {
|
||||
if (!looks_like_heap_obj(m)) return NULL;
|
||||
ElManifold* p = (ElManifold*)(uintptr_t)m;
|
||||
if (p->hdr.magic != EL_MAGIC_MFLD) return NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
static int mfld_find(ElManifold* p, const char* key) {
|
||||
for (size_t i = 0; i < p->ncomp; i++)
|
||||
if (strcmp(p->comps[i].key, key) == 0) return (int)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
el_val_t manifold_new(void) {
|
||||
ElManifold* p = (ElManifold*)calloc(1, sizeof(ElManifold));
|
||||
if (!p) return (el_val_t)0;
|
||||
p->hdr.magic = EL_MAGIC_MFLD;
|
||||
p->hdr.refcount = 1;
|
||||
return (el_val_t)(uintptr_t)p;
|
||||
}
|
||||
|
||||
el_val_t manifold_is(el_val_t m) {
|
||||
return mfld_of(m) ? (el_val_t)1 : (el_val_t)0;
|
||||
}
|
||||
|
||||
/* manifold_add — add one COMPONENT: a named part with its own geometry.
|
||||
* Returns the component's index, or -1 on any refusal. Refusals are real and
|
||||
* distinct: an empty key (unaddressable), a duplicate key (ambiguous
|
||||
* addressing), a value that is not a live Geometry (a part with no geometry is
|
||||
* not a part). Each is a caller error worth surfacing at the point of the
|
||||
* mistake rather than as a missing node three layers downstream. */
|
||||
el_val_t manifold_add(el_val_t m, el_val_t key, el_val_t role, el_val_t g) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
if (!p) return (el_val_t)(int64_t)-1;
|
||||
const char* k = EL_CSTR(key);
|
||||
const char* r = EL_CSTR(role);
|
||||
if (!k || !*k) return (el_val_t)(int64_t)-1;
|
||||
if (!r) r = "";
|
||||
ElGeometry* src = geom_of(g);
|
||||
if (!src || src->dim <= 0) return (el_val_t)(int64_t)-1;
|
||||
if (mfld_find(p, k) >= 0) return (el_val_t)(int64_t)-1; /* duplicate key */
|
||||
|
||||
if (p->ncomp == p->capcomp) {
|
||||
size_t nc = p->capcomp ? p->capcomp * 2 : 8;
|
||||
ElComponent* nb = (ElComponent*)realloc(p->comps, nc * sizeof(ElComponent));
|
||||
if (!nb) return (el_val_t)(int64_t)-1;
|
||||
p->comps = nb; p->capcomp = nc;
|
||||
}
|
||||
|
||||
/* COPY the payload — a component's geometry must not alias the caller's. */
|
||||
ElGeometry* cp = (ElGeometry*)malloc(sizeof(ElGeometry));
|
||||
if (!cp) return (el_val_t)(int64_t)-1;
|
||||
cp->v = (float*)malloc(sizeof(float) * (size_t)src->dim);
|
||||
if (!cp->v) { free(cp); return (el_val_t)(int64_t)-1; }
|
||||
memcpy(cp->v, src->v, sizeof(float) * (size_t)src->dim);
|
||||
cp->hdr.magic = EL_MAGIC_GEOM;
|
||||
cp->hdr.refcount = 1;
|
||||
cp->dim = src->dim;
|
||||
|
||||
p->comps[p->ncomp].key = el_strdup_persist(k);
|
||||
p->comps[p->ncomp].role = el_strdup_persist(r);
|
||||
p->comps[p->ncomp].g = cp;
|
||||
p->ncomp++;
|
||||
return (el_val_t)(int64_t)(p->ncomp - 1);
|
||||
}
|
||||
|
||||
/* manifold_relate — state a relation BETWEEN two components. This is the part
|
||||
* that carries the meaning: the components are the parts, the relations are
|
||||
* what the thing IS.
|
||||
*
|
||||
* Both endpoints must already exist. An edge to a name that was never added is
|
||||
* refused with 0, never silently discarded — see the header note. */
|
||||
el_val_t manifold_relate(el_val_t m, el_val_t from, el_val_t rel,
|
||||
el_val_t to, el_val_t weight) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
if (!p) return (el_val_t)0;
|
||||
const char* f = EL_CSTR(from);
|
||||
const char* r = EL_CSTR(rel);
|
||||
const char* t = EL_CSTR(to);
|
||||
if (!f || !*f || !r || !*r || !t || !*t) return (el_val_t)0;
|
||||
if (mfld_find(p, f) < 0) return (el_val_t)0;
|
||||
if (mfld_find(p, t) < 0) return (el_val_t)0;
|
||||
|
||||
if (p->nrel == p->caprel) {
|
||||
size_t nc = p->caprel ? p->caprel * 2 : 8;
|
||||
ElRelation* nb = (ElRelation*)realloc(p->rels, nc * sizeof(ElRelation));
|
||||
if (!nb) return (el_val_t)0;
|
||||
p->rels = nb; p->caprel = nc;
|
||||
}
|
||||
p->rels[p->nrel].from = el_strdup_persist(f);
|
||||
p->rels[p->nrel].rel = el_strdup_persist(r);
|
||||
p->rels[p->nrel].to = el_strdup_persist(t);
|
||||
p->rels[p->nrel].weight = el_to_float(weight);
|
||||
p->nrel++;
|
||||
return (el_val_t)1;
|
||||
}
|
||||
|
||||
el_val_t manifold_size(el_val_t m) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
return p ? (el_val_t)(int64_t)p->ncomp : (el_val_t)0;
|
||||
}
|
||||
|
||||
el_val_t manifold_rel_count(el_val_t m) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
return p ? (el_val_t)(int64_t)p->nrel : (el_val_t)0;
|
||||
}
|
||||
|
||||
/* Index of a component BY KEY, or -1. This is the addressability primitive:
|
||||
* everything downstream that wants to ground, weight or contradict one part
|
||||
* finds it through here. */
|
||||
el_val_t manifold_index_of(el_val_t m, el_val_t key) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
const char* k = EL_CSTR(key);
|
||||
if (!p || !k || !*k) return (el_val_t)(int64_t)-1;
|
||||
return (el_val_t)(int64_t)mfld_find(p, k);
|
||||
}
|
||||
|
||||
el_val_t manifold_key(el_val_t m, el_val_t i) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
int64_t k = (int64_t)i;
|
||||
if (!p || k < 0 || k >= (int64_t)p->ncomp) return el_wrap_str(el_strdup(""));
|
||||
return el_wrap_str(el_strdup(p->comps[k].key));
|
||||
}
|
||||
|
||||
el_val_t manifold_role(el_val_t m, el_val_t i) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
int64_t k = (int64_t)i;
|
||||
if (!p || k < 0 || k >= (int64_t)p->ncomp) return el_wrap_str(el_strdup(""));
|
||||
return el_wrap_str(el_strdup(p->comps[k].role));
|
||||
}
|
||||
|
||||
/* manifold_geometry — the geometry OF ONE COMPONENT, as a fresh Geometry the
|
||||
* caller owns and frees. A borrowed interior pointer would let a caller's
|
||||
* geometry_free corrupt the manifold; copying is the same discipline
|
||||
* node_attach_geometry already applies in the other direction. */
|
||||
el_val_t manifold_geometry(el_val_t m, el_val_t i) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
int64_t k = (int64_t)i;
|
||||
if (!p || k < 0 || k >= (int64_t)p->ncomp) return (el_val_t)0;
|
||||
ElGeometry* src = p->comps[k].g;
|
||||
el_val_t out = geometry_new((el_val_t)(int64_t)src->dim);
|
||||
ElGeometry* dst = geom_of(out);
|
||||
if (!dst) return (el_val_t)0;
|
||||
memcpy(dst->v, src->v, sizeof(float) * (size_t)src->dim);
|
||||
return out;
|
||||
}
|
||||
|
||||
el_val_t manifold_rel_from(el_val_t m, el_val_t j) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
int64_t k = (int64_t)j;
|
||||
if (!p || k < 0 || k >= (int64_t)p->nrel) return el_wrap_str(el_strdup(""));
|
||||
return el_wrap_str(el_strdup(p->rels[k].from));
|
||||
}
|
||||
|
||||
el_val_t manifold_rel_name(el_val_t m, el_val_t j) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
int64_t k = (int64_t)j;
|
||||
if (!p || k < 0 || k >= (int64_t)p->nrel) return el_wrap_str(el_strdup(""));
|
||||
return el_wrap_str(el_strdup(p->rels[k].rel));
|
||||
}
|
||||
|
||||
el_val_t manifold_rel_to(el_val_t m, el_val_t j) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
int64_t k = (int64_t)j;
|
||||
if (!p || k < 0 || k >= (int64_t)p->nrel) return el_wrap_str(el_strdup(""));
|
||||
return el_wrap_str(el_strdup(p->rels[k].to));
|
||||
}
|
||||
|
||||
el_val_t manifold_rel_weight(el_val_t m, el_val_t j) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
int64_t k = (int64_t)j;
|
||||
if (!p || k < 0 || k >= (int64_t)p->nrel) return el_from_float(0.0);
|
||||
return el_from_float(p->rels[k].weight);
|
||||
}
|
||||
|
||||
/* manifold_single — the DEGENERATE case, expressible but visibly degenerate.
|
||||
*
|
||||
* Sometimes a modality really does have one part (a scalar sensor). That is a
|
||||
* manifold of size 1, not a different kind of thing, and writing it this way
|
||||
* keeps the fingerprint as a SPECIAL CASE of decomposition rather than a
|
||||
* parallel path back to #144's contract. Anything reading it still asks
|
||||
* manifold_size and still gets a real answer. */
|
||||
el_val_t manifold_single(el_val_t key, el_val_t role, el_val_t g) {
|
||||
el_val_t m = manifold_new();
|
||||
if (!mfld_of(m)) return (el_val_t)0;
|
||||
if ((int64_t)manifold_add(m, key, role, g) < 0) { manifold_free(m); return (el_val_t)0; }
|
||||
return m;
|
||||
}
|
||||
|
||||
el_val_t manifold_free(el_val_t m) {
|
||||
ElManifold* p = mfld_of(m);
|
||||
if (!p) return (el_val_t)0;
|
||||
for (size_t i = 0; i < p->ncomp; i++) {
|
||||
free(p->comps[i].key);
|
||||
free(p->comps[i].role);
|
||||
if (p->comps[i].g) { free(p->comps[i].g->v); p->comps[i].g->hdr.magic = 0; free(p->comps[i].g); }
|
||||
}
|
||||
for (size_t i = 0; i < p->nrel; i++) {
|
||||
free(p->rels[i].from); free(p->rels[i].rel); free(p->rels[i].to);
|
||||
}
|
||||
free(p->comps);
|
||||
free(p->rels);
|
||||
p->hdr.magic = 0; /* poison, as Geometry/List/Map do */
|
||||
free(p);
|
||||
return (el_val_t)1;
|
||||
}
|
||||
|
||||
/* ── Realizers: transduction declared in El, not patched into the runtime ────
|
||||
*
|
||||
* A REALIZER maps one modality into geometry. The whole reason transduction
|
||||
* belongs in the language is that ADDING A MODALITY MUST NOT REQUIRE A
|
||||
* RUNTIME PATCH — otherwise "the realizers are in the engram" just becomes
|
||||
* "the realizers are in the runtime" and nothing has actually moved. So
|
||||
* realizers are declared in El and registered by NAME:
|
||||
* A REALIZER DECOMPOSES one modality into components and their relations. It
|
||||
* does not encode a signal to a point — that is the operation one layer below
|
||||
* it, and it is called geometry, not transduction. A realizer for a modality
|
||||
* declares what that modality's COMPONENTS ARE: for audio, not one MFCC
|
||||
* vector, but pitch, interval, rhythm, harmonic function, and how they stand
|
||||
* to one another.
|
||||
*
|
||||
* fn tone_realizer(signal: String) -> Geometry {
|
||||
* let g: Geometry = geometry_new(8)
|
||||
* ... geometry_set(g, i, x) ...
|
||||
* g
|
||||
* The whole reason transduction belongs in the language is that ADDING A
|
||||
* MODALITY MUST NOT REQUIRE A RUNTIME PATCH — otherwise "the realizers are in
|
||||
* the engram" just becomes "the realizers are in the runtime" and nothing has
|
||||
* actually moved. So realizers are declared in El and registered by NAME:
|
||||
*
|
||||
* fn tone_realizer(signal: String) -> Manifold {
|
||||
* let m: Manifold = manifold_new()
|
||||
* let a: Int = manifold_add(m, "pitch", "spectral", pitch_geom)
|
||||
* let b: Int = manifold_add(m, "interval", "relation", interval_geom)
|
||||
* let e: Int = manifold_relate(m, "pitch", "spans", "interval", 0.9)
|
||||
* m
|
||||
* }
|
||||
*
|
||||
* realizer_register("tone", "tone_realizer")
|
||||
* let g: Geometry = transduce(sample, "tone")
|
||||
* let m: Manifold = transduce(sample, "tone")
|
||||
*
|
||||
* A realizer's DECLARED COMPONENT VOCABULARY is the interesting part of its
|
||||
* contract, and it is what a caller can then ground, weight and contradict
|
||||
* one part at a time.
|
||||
*
|
||||
* The name→symbol step rides the identical, already load-bearing mechanism
|
||||
* http_set_handler uses (see "HTTP server"): every El `fn name(...)` compiles
|
||||
@@ -6380,28 +6677,45 @@ el_val_t realizer_has(el_val_t modality) {
|
||||
return realizer_lookup(m) ? (el_val_t)1 : (el_val_t)0;
|
||||
}
|
||||
|
||||
/* transduce — THE primitive: signal in, geometry out.
|
||||
/* transduce — THE primitive: signal in, SUBGRAPH out.
|
||||
*
|
||||
* Dispatches to the realizer registered for `modality`. Returns 0 (not a
|
||||
* Geometry) when no realizer is registered, and geometry_is() on the result
|
||||
* is the check.
|
||||
* Manifold) when no realizer is registered, and manifold_is() on the result is
|
||||
* the check.
|
||||
*
|
||||
* THE RETURN TYPE IS THE CORRECTION. #144 shipped this as
|
||||
* `transduce(signal, modality) -> Geometry` — one vector out. That made
|
||||
* transduction a CONVERSION: take a thing, encode it, store a position. What
|
||||
* comes back from a conversion is a fingerprint, and a fingerprint supports
|
||||
* exactly two operations, match and rank. It cannot be decomposed, cannot have
|
||||
* one part grounded while another is not, and cannot be contradicted in a part
|
||||
* — it has no parts. Transduction is not conversion. It is DECOMPOSITION into
|
||||
* components plus the relations among them, and the relations are the content.
|
||||
* See the Manifold header above.
|
||||
*
|
||||
* There is deliberately NO built-in realizer, not even for text. A modality
|
||||
* the program has declared no organ for is one it genuinely cannot sense,
|
||||
* and returning nothing is more honest than quietly embedding a description
|
||||
* of the signal and calling that perception — which is the exact failure
|
||||
* this whole change exists to end.
|
||||
* the program has declared no organ for is one it genuinely cannot sense, and
|
||||
* returning nothing is more honest than quietly embedding a description of the
|
||||
* signal and calling that perception — the failure #144 named, and which a
|
||||
* single-vector return type quietly reintroduced one level down: a
|
||||
* one-vector-per-signal organ is a description of the signal, not a perception
|
||||
* of it.
|
||||
*
|
||||
* The result is validated to actually BE a Geometry before it is handed
|
||||
* back, so a realizer that returns something else transduced nothing rather
|
||||
* than handing a caller a value that will misbehave far from here. */
|
||||
* The result is validated to actually BE a Manifold before it is handed back.
|
||||
* A realizer still returning a bare Geometry — #144's contract — therefore
|
||||
* transduces NOTHING rather than handing back a value that decomposes to
|
||||
* nothing far from here. That is a deliberate hard failure, not an oversight:
|
||||
* "no organ" and "an organ that only fingerprints" must not look alike, which
|
||||
* is the same distinction realizer_register draws between an absent and a
|
||||
* broken organ. A realizer with genuinely one part says so with
|
||||
* manifold_single. */
|
||||
el_val_t transduce(el_val_t signal, el_val_t modality) {
|
||||
const char* m = EL_CSTR(modality);
|
||||
if (!m || !*m) return (el_val_t)0;
|
||||
el_realizer_fn fn = realizer_lookup(m);
|
||||
if (!fn) return (el_val_t)0;
|
||||
el_val_t g = fn(signal);
|
||||
return geom_of(g) ? g : (el_val_t)0;
|
||||
return mfld_of(g) ? g : (el_val_t)0;
|
||||
}
|
||||
|
||||
/* ── Batch 3: Engram in-process graph store ──────────────────────────────── */
|
||||
|
||||
+60
-10
@@ -625,20 +625,70 @@ el_val_t geometry_free(el_val_t g); /* 1 if freed, 0 if not a
|
||||
el_val_t geometry_from_f32le_hex(el_val_t hex); /* 0 on empty/odd-length/non-hex */
|
||||
el_val_t geometry_to_f32le_hex(el_val_t g); /* "" if not a Geometry */
|
||||
|
||||
/* ── Realizers + transduce ───────────────────────────────────────────────────
|
||||
* A REALIZER maps one modality into geometry. Registration is by NAME, so a
|
||||
* new modality never requires a runtime patch: every El `fn name(...)`
|
||||
* compiles to a global C symbol with that exact name, and the registry
|
||||
* resolves it with dlsym against the running binary — the same mechanism
|
||||
* http_set_handler already relies on.
|
||||
/* ── Manifold: the result of a transduction ──────────────────────────────────
|
||||
* A transduced signal is a SUBGRAPH — named components, each with its own
|
||||
* geometry, plus typed weighted relations among them — not a single vector.
|
||||
* One vector is a fingerprint: matchable, rankable, and nothing else. A song
|
||||
* decomposes into pitch, interval, rhythm, harmonic function; the song IS the
|
||||
* structure of those relations, and collapsing it to a point discards exactly
|
||||
* what made it reasonable-about. See el_runtime.c ("Manifold") for the full
|
||||
* rationale, the key-addressing rule, and the ownership contract.
|
||||
*
|
||||
* fn tone_realizer(signal: String) -> Geometry { ... }
|
||||
* Components are addressed BY KEY, never by index, because the key is what
|
||||
* survives persistence: a component becomes a node, and it is separately
|
||||
* groundable precisely because it is separately named. Relation weight IS the
|
||||
* grounding (correspondence-and-censorship.md §1) — one quantity, no separate
|
||||
* score, nothing computed on read.
|
||||
*
|
||||
* OWNERSHIP: a Manifold is owned by the El caller and released with
|
||||
* manifold_free, which also releases every component's geometry. manifold_add
|
||||
* COPIES the geometry it is given and manifold_geometry RETURNS a copy, so no
|
||||
* component's vector is ever aliased in either direction. */
|
||||
el_val_t manifold_new(void); /* empty; 0 on failure */
|
||||
el_val_t manifold_is(el_val_t m); /* 1 if a live Manifold */
|
||||
el_val_t manifold_add(el_val_t m, el_val_t key, el_val_t role, el_val_t g);
|
||||
/* component index, or -1 on empty/duplicate
|
||||
* key or a value that is not a Geometry */
|
||||
el_val_t manifold_relate(el_val_t m, el_val_t from, el_val_t rel,
|
||||
el_val_t to, el_val_t weight);
|
||||
/* 1 ok / 0 if either endpoint is unknown —
|
||||
* an unresolvable edge is REFUSED, never
|
||||
* silently dropped */
|
||||
el_val_t manifold_size(el_val_t m); /* component count */
|
||||
el_val_t manifold_rel_count(el_val_t m); /* relation count */
|
||||
el_val_t manifold_index_of(el_val_t m, el_val_t key); /* index by key, or -1 */
|
||||
el_val_t manifold_key(el_val_t m, el_val_t i); /* "" if out of range */
|
||||
el_val_t manifold_role(el_val_t m, el_val_t i); /* "" if out of range */
|
||||
el_val_t manifold_geometry(el_val_t m, el_val_t i); /* a COPY the caller frees */
|
||||
el_val_t manifold_rel_from(el_val_t m, el_val_t j); /* source component key */
|
||||
el_val_t manifold_rel_name(el_val_t m, el_val_t j); /* relation name */
|
||||
el_val_t manifold_rel_to(el_val_t m, el_val_t j); /* target component key */
|
||||
el_val_t manifold_rel_weight(el_val_t m, el_val_t j); /* Float — the grounding */
|
||||
el_val_t manifold_single(el_val_t key, el_val_t role, el_val_t g);
|
||||
/* the degenerate one-part case, expressible
|
||||
* but visibly a size-1 manifold rather than
|
||||
* a parallel path back to a bare vector */
|
||||
el_val_t manifold_free(el_val_t m); /* 1 if freed, 0 otherwise */
|
||||
|
||||
/* ── Realizers + transduce ───────────────────────────────────────────────────
|
||||
* A REALIZER DECOMPOSES one modality into components and relations. It does
|
||||
* not encode a signal to a point; that operation is one layer below and is
|
||||
* called geometry. Registration is by NAME, so a new modality never requires a
|
||||
* runtime patch: every El `fn name(...)` compiles to a global C symbol with
|
||||
* that exact name, and the registry resolves it with dlsym against the running
|
||||
* binary — the same mechanism http_set_handler already relies on.
|
||||
*
|
||||
* fn tone_realizer(signal: String) -> Manifold { ... }
|
||||
* realizer_register("tone", "tone_realizer")
|
||||
* let g: Geometry = transduce(sample, "tone")
|
||||
*/
|
||||
* let m: Manifold = transduce(sample, "tone")
|
||||
*
|
||||
* SUPERSEDES #144's `transduce -> Geometry`. A realizer that still returns a
|
||||
* bare Geometry now transduces NOTHING (transduce returns 0), deliberately: an
|
||||
* organ that only fingerprints must not be indistinguishable from a working
|
||||
* one. A modality with genuinely one part says so with manifold_single. */
|
||||
el_val_t realizer_register(el_val_t modality, el_val_t fn_name); /* 1 ok / 0 unresolved */
|
||||
el_val_t realizer_has(el_val_t modality); /* 1 if a realizer is registered */
|
||||
el_val_t transduce(el_val_t signal, el_val_t modality); /* Geometry, or 0 if no organ */
|
||||
el_val_t transduce(el_val_t signal, el_val_t modality); /* Manifold, or 0 if no organ */
|
||||
|
||||
/* ── Engram local graph primitives ───────────────────────────────────────────
|
||||
* Operate on the CGI's local Engram knowledge graph.
|
||||
|
||||
+440
-140
@@ -1,61 +1,128 @@
|
||||
import "../../runtime/eltest.el"
|
||||
// test_transduce.el — geometry as a first-class El value, and realizers
|
||||
// declared in El rather than patched into the runtime.
|
||||
// test_transduce.el — transduction produces a SUBGRAPH, not a point.
|
||||
//
|
||||
// WHAT IS ACTUALLY UNDER TEST. Until 2026-08-16 no El ingest path could carry
|
||||
// a vector: nodes took text, and geometry was DERIVED from that text. Text was
|
||||
// therefore the mandatory entry medium, so any non-text modality had to be
|
||||
// DESCRIBED in prose first and the geometry we reasoned over was the geometry
|
||||
// OF THE DESCRIPTION, not of the signal. The fix has two halves, and this file
|
||||
// exercises both:
|
||||
// WHAT IS ACTUALLY UNDER TEST. #144 moved transduction into the language and
|
||||
// got the dispatch right: realizers declared in El, resolved by name, no
|
||||
// runtime patch per modality. It got the RESULT TYPE wrong —
|
||||
// `transduce(signal, modality) -> Geometry`, one vector per signal.
|
||||
//
|
||||
// 1. Geometry is a VALUE — it carries its own width, so nothing has to
|
||||
// assert a width against a string's length.
|
||||
// 2. A REALIZER is an ordinary El function. `tone_realizer` below is not in
|
||||
// the runtime, is not known to the compiler, and is not special in any
|
||||
// way; it is registered BY NAME and dispatched to through transduce().
|
||||
// That is the load-bearing claim: adding a modality must not require a
|
||||
// runtime patch, or nothing has actually moved into the language.
|
||||
// One vector is a FINGERPRINT. It can be matched and it can be ranked, and
|
||||
// that is the whole of what it can ever do. It cannot be decomposed, cannot
|
||||
// have one part grounded while another is not, and cannot be contradicted in
|
||||
// one part while holding in another — because it has no parts. Treating
|
||||
// transduction as a CONVERSION (signal in, position out) is the premise this
|
||||
// file exists to falsify.
|
||||
//
|
||||
// A song is not a point. It decomposes into pitch, interval, rhythm, harmonic
|
||||
// function — components, each with its own geometry, plus the relations among
|
||||
// them. THE SONG IS THE STRUCTURE OF THE RELATIONS. So transduction yields a
|
||||
// Manifold: named components carrying geometry, and typed weighted relations
|
||||
// between them.
|
||||
//
|
||||
// The geometry tests below are UNCHANGED from #144 and still pass, which is
|
||||
// the point: Geometry was never wrong, it was misplaced. A vector is the right
|
||||
// representation for a COMPONENT. It was only ever wrong as the representation
|
||||
// of a whole transduced signal.
|
||||
//
|
||||
// COMPARISON DISCIPLINE IN THIS FILE (measured 2026-08-16, not stylistic):
|
||||
// elc lowers `a == b` to a NUMERIC comparison only when both operand names are
|
||||
// in the per-function int-name set, which `let x: Int` populates. A bare call
|
||||
// like `geometry_is(g) == 0` is not a registered name, so it lowers to
|
||||
// like `manifold_size(m) == 5` is not a registered name, so it lowers to
|
||||
// `str_eq(...)` — strcmp on two integers reinterpreted as pointers. `<` and `>`
|
||||
// lower directly via binop_to_c with no type inference at all, so truthiness is
|
||||
// written `> 0` / `< 1` here, and any exact `==` is done on a value first bound
|
||||
// through `let x: Int`.
|
||||
//
|
||||
// ONE FURTHER RULE, measured while writing this file: that int-name set LEAKS
|
||||
// ACROSS `test` BLOCKS. Binding `dn` as a Float in one test and as an Int in
|
||||
// another silently demoted the Int comparison to str_eq and failed an
|
||||
// assertion that was arithmetically true. Every Int-bound name compared with
|
||||
// `==` here is therefore spelled UNIQUELY across the whole file (note_dim,
|
||||
// iv_dim, ...), rather than reusing a short name per test.
|
||||
|
||||
// ── A realizer, written entirely in El ──────────────────────────────────────
|
||||
// Maps a "tone" signal into a 4-component geometry. Deliberately trivial —
|
||||
// what is being proven is that an El function can BE a realizer, not that
|
||||
// this is good acoustics. The one real property it has: distinct signals
|
||||
// produce distinct geometry, so the test can tell transduction from a stub.
|
||||
fn tone_realizer(signal: String) -> Geometry {
|
||||
// ── A DECOMPOSING realizer, written entirely in El ──────────────────────────
|
||||
// "tone" signals are note letters, e.g. "CEG". This realizer does NOT return
|
||||
// one vector for the chord. It returns the PARTS — one component per note, one
|
||||
// per interval between adjacent notes — and the relations that make those
|
||||
// parts a chord rather than an unordered bag of pitches.
|
||||
//
|
||||
// The interval is deliberately a COMPONENT, not an attribute of a note. An
|
||||
// interval is a thing with its own geometry that belongs to neither endpoint;
|
||||
// modelling it as a field on a note is exactly the collapse this change
|
||||
// rejects, one level down.
|
||||
fn tone_realizer(signal: String) -> Manifold {
|
||||
let m: Manifold = manifold_new()
|
||||
let n: Int = str_len(signal)
|
||||
|
||||
let i: Int = 0
|
||||
while i < n {
|
||||
let code: Int = str_char_code(signal, i)
|
||||
let g: Geometry = geometry_new(2)
|
||||
let s0: Int = geometry_set(g, 0, int_to_float(code))
|
||||
let s1: Int = geometry_set(g, 1, int_to_float(i))
|
||||
let idx: Int = manifold_add(m, "note:" + int_to_str(i), "pitch", g)
|
||||
let f: Int = geometry_free(g)
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
let j: Int = 1
|
||||
while j < n {
|
||||
let a: Int = str_char_code(signal, j - 1)
|
||||
let b: Int = str_char_code(signal, j)
|
||||
let lo: String = "note:" + int_to_str(j - 1)
|
||||
let hi: String = "note:" + int_to_str(j)
|
||||
let key: String = "interval:" + int_to_str(j - 1) + "-" + int_to_str(j)
|
||||
let g: Geometry = geometry_new(1)
|
||||
let s: Int = geometry_set(g, 0, int_to_float(b - a))
|
||||
let idx: Int = manifold_add(m, key, "interval", g)
|
||||
let f: Int = geometry_free(g)
|
||||
let e1: Int = manifold_relate(m, key, "spans", lo, 0.9)
|
||||
let e2: Int = manifold_relate(m, key, "spans", hi, 0.9)
|
||||
let e3: Int = manifold_relate(m, lo, "sounds_before", hi, 0.8)
|
||||
j = j + 1
|
||||
}
|
||||
m
|
||||
}
|
||||
|
||||
// A second realizer for a different modality, to prove the registry keys on
|
||||
// modality and does not just hand back "the last thing registered". Its
|
||||
// decomposition has a DIFFERENT shape — two components, one relation — so a
|
||||
// test can tell the two organs apart by structure alone.
|
||||
fn pulse_realizer(signal: String) -> Manifold {
|
||||
let m: Manifold = manifold_new()
|
||||
let ga: Geometry = geometry_new(1)
|
||||
let sa: Int = geometry_set(ga, 0, 1.0)
|
||||
let ia: Int = manifold_add(m, "onset", "event", ga)
|
||||
let fa: Int = geometry_free(ga)
|
||||
let gb: Geometry = geometry_new(1)
|
||||
let sb: Int = geometry_set(gb, 0, 0.0)
|
||||
let ib: Int = manifold_add(m, "decay", "envelope", gb)
|
||||
let fb: Int = geometry_free(gb)
|
||||
let e: Int = manifold_relate(m, "onset", "decays_into", "decay", 0.7)
|
||||
m
|
||||
}
|
||||
|
||||
// #144's ACTUAL CONTRACT, preserved verbatim as a control: a realizer that
|
||||
// returns one vector for the whole signal. This is not a strawman — it is what
|
||||
// the merged primitive asked realizers to be. It must now transduce NOTHING.
|
||||
fn fingerprint_realizer(signal: String) -> Geometry {
|
||||
let g: Geometry = geometry_new(4)
|
||||
let n: Int = str_len(signal)
|
||||
let a: Int = geometry_set(g, 0, int_to_float(n))
|
||||
let b: Int = geometry_set(g, 1, int_to_float(n * 2))
|
||||
let c: Int = geometry_set(g, 2, int_to_float(n * 3))
|
||||
let d: Int = geometry_set(g, 3, int_to_float(n * 4))
|
||||
g
|
||||
}
|
||||
|
||||
// A second realizer for a different modality, to prove the registry keys on
|
||||
// modality and does not just hand back "the last thing registered".
|
||||
fn pulse_realizer(signal: String) -> Geometry {
|
||||
let g: Geometry = geometry_new(2)
|
||||
let a: Int = geometry_set(g, 0, 1.0)
|
||||
let b: Int = geometry_set(g, 1, 0.0)
|
||||
g
|
||||
}
|
||||
|
||||
// A deliberately BROKEN realizer: it returns something that is not a Geometry.
|
||||
// transduce() must not hand this back to a caller as if it were one.
|
||||
fn bogus_realizer(signal: String) -> Geometry {
|
||||
// A realizer returning something that is not a value at all.
|
||||
fn bogus_realizer(signal: String) -> Manifold {
|
||||
return 12345
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Geometry — unchanged from #144. A vector is the right representation for a
|
||||
// COMPONENT; it was only ever wrong as the representation of a whole signal.
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
test "geometry-is-a-value-with-its-own-width" {
|
||||
let g: Geometry = geometry_new(8)
|
||||
let live: Int = geometry_is(g)
|
||||
@@ -67,17 +134,12 @@ test "geometry-is-a-value-with-its-own-width" {
|
||||
}
|
||||
|
||||
test "geometry-rejects-nonsense-without-an-arbitrary-bound" {
|
||||
// dim <= 0 is not a width. Note there is deliberately no MAX dim here:
|
||||
// #141 needed `dim <= 8192` only to bound an allocation sized from a
|
||||
// caller's claim about a string. A value that carries its own width has
|
||||
// nothing left to validate, so the only failure left is allocation.
|
||||
let zero: Geometry = geometry_new(0)
|
||||
let z: Int = geometry_is(zero)
|
||||
assert z < 1, "dim 0 is not a geometry"
|
||||
let neg: Geometry = geometry_new(-4)
|
||||
let n: Int = geometry_is(neg)
|
||||
assert n < 1, "negative dim is not a geometry"
|
||||
// Accessors must be total: a non-geometry is 0-width, never a crash.
|
||||
let nd: Int = geometry_dim(0)
|
||||
assert nd < 1, "geometry_dim of a non-geometry is 0"
|
||||
let ni: Int = geometry_is(0)
|
||||
@@ -105,21 +167,11 @@ test "geometry-components-round-trip" {
|
||||
}
|
||||
|
||||
test "hex-is-an-edge-adapter-and-derives-its-own-width" {
|
||||
// 2 components, little-endian float32: 1.0 = 0000803f, 2.0 = 00000040.
|
||||
let g: Geometry = geometry_from_f32le_hex("0000803f00000040")
|
||||
let live: Int = geometry_is(g)
|
||||
assert live > 0, "valid hex decodes to a Geometry"
|
||||
let d: Int = geometry_dim(g)
|
||||
assert d == 2, "width is DERIVED from the input, never supplied"
|
||||
let a: Float = geometry_get(g, 0)
|
||||
let da: Float = a - 1.0
|
||||
assert da < 0.001, "first component decoded"
|
||||
assert da > -0.001, "first component decoded"
|
||||
let b: Float = geometry_get(g, 1)
|
||||
let db: Float = b - 2.0
|
||||
assert db < 0.001, "second component decoded"
|
||||
assert db > -0.001, "second component decoded"
|
||||
// Egress adapter is the exact inverse.
|
||||
let hex_dim: Int = geometry_dim(g)
|
||||
assert hex_dim == 2, "width is DERIVED from the input, never supplied"
|
||||
let back: String = geometry_to_f32le_hex(g)
|
||||
assert str_eq(back, "0000803f00000040"), "hex round-trips exactly"
|
||||
let freed: Int = geometry_free(g)
|
||||
@@ -137,98 +189,346 @@ test "hex-rejects-malformed-input" {
|
||||
assert nh < 1, "non-hex characters are refused"
|
||||
}
|
||||
|
||||
test "a-realizer-declared-in-el-is-a-first-class-realizer" {
|
||||
// THE CLAIM: tone_realizer is an ordinary El function. It is not in the
|
||||
// runtime and the compiler knows nothing about it. Registering it by name
|
||||
// is enough to make it the organ for a modality.
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
assert reg > 0, "an El fn registers as a realizer by name"
|
||||
let has: Int = realizer_has("tone")
|
||||
assert has > 0, "the modality now has an organ"
|
||||
|
||||
let g: Geometry = transduce("aaa", "tone")
|
||||
let live: Int = geometry_is(g)
|
||||
assert live > 0, "transduce returns real geometry"
|
||||
let d: Int = geometry_dim(g)
|
||||
assert d == 4, "the El realizer determined the width, not the runtime"
|
||||
// str_len("aaa") == 3, so component 0 must be 3.0 — proof the signal
|
||||
// actually reached the El function rather than a stub answering for it.
|
||||
let c0: Float = geometry_get(g, 0)
|
||||
let dc: Float = c0 - 3.0
|
||||
assert dc < 0.001, "the signal reached the El realizer"
|
||||
assert dc > -0.001, "the signal reached the El realizer"
|
||||
let freed: Int = geometry_free(g)
|
||||
}
|
||||
|
||||
test "distinct-signals-transduce-to-distinct-geometry" {
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
let g1: Geometry = transduce("aa", "tone")
|
||||
let g2: Geometry = transduce("aaaaa", "tone")
|
||||
let a: Float = geometry_get(g1, 0)
|
||||
let b: Float = geometry_get(g2, 0)
|
||||
let diff: Float = b - a
|
||||
// 5 - 2 = 3. If transduction were a stub these would be equal.
|
||||
assert diff > 2.9, "different signals produce different geometry"
|
||||
assert diff < 3.1, "different signals produce different geometry"
|
||||
let f1: Int = geometry_free(g1)
|
||||
let f2: Int = geometry_free(g2)
|
||||
}
|
||||
|
||||
test "the-registry-keys-on-modality" {
|
||||
let r1: Int = realizer_register("tone", "tone_realizer")
|
||||
let r2: Int = realizer_register("pulse", "pulse_realizer")
|
||||
assert r2 > 0, "a second modality registers independently"
|
||||
let gt: Geometry = transduce("aaa", "tone")
|
||||
let gp: Geometry = transduce("aaa", "pulse")
|
||||
let dt: Int = geometry_dim(gt)
|
||||
let dp: Int = geometry_dim(gp)
|
||||
assert dt == 4, "tone still routes to its own realizer"
|
||||
assert dp == 2, "pulse routes to a different realizer"
|
||||
let f1: Int = geometry_free(gt)
|
||||
let f2: Int = geometry_free(gp)
|
||||
}
|
||||
|
||||
test "no-organ-is-reported-as-no-organ" {
|
||||
// A modality with no realizer must transduce to NOTHING. It must never
|
||||
// fall back to embedding a description of the signal and calling that
|
||||
// perception — that silent substitution is the entire defect this change
|
||||
// exists to end.
|
||||
let has: Int = realizer_has("echolocation")
|
||||
assert has < 1, "unregistered modality has no organ"
|
||||
let g: Geometry = transduce("anything", "echolocation")
|
||||
let live: Int = geometry_is(g)
|
||||
assert live < 1, "no realizer means no geometry, not fake geometry"
|
||||
}
|
||||
|
||||
test "registration-of-an-unresolvable-name-fails-loudly" {
|
||||
// Reported at the moment of WIRING, not later as "this modality mysteriously
|
||||
// produces nothing". Distinguishing "no organ" from "broken organ" is the
|
||||
// lesson that made this whole change necessary.
|
||||
let bad: Int = realizer_register("ghost", "no_such_function_anywhere")
|
||||
assert bad < 1, "an unresolvable realizer name is a registration failure"
|
||||
let has: Int = realizer_has("ghost")
|
||||
assert has < 1, "and nothing gets registered"
|
||||
}
|
||||
|
||||
test "a-realizer-returning-non-geometry-transduces-nothing" {
|
||||
let reg: Int = realizer_register("bogus", "bogus_realizer")
|
||||
assert reg > 0, "the symbol resolves, so registration succeeds"
|
||||
// ...but the contract is enforced at the boundary, so the caller never
|
||||
// receives a value that would misbehave far away from here.
|
||||
let g: Geometry = transduce("x", "bogus")
|
||||
let live: Int = geometry_is(g)
|
||||
assert live < 1, "a non-Geometry return transduced nothing"
|
||||
}
|
||||
|
||||
test "norm-lets-a-caller-check-a-realizer-emitted-signal" {
|
||||
let g: Geometry = geometry_new(2)
|
||||
let z: Float = geometry_norm(g)
|
||||
assert z < 0.001, "a fresh geometry is zero — norm says so"
|
||||
let s0: Int = geometry_set(g, 0, 3.0)
|
||||
let s1: Int = geometry_set(g, 1, 4.0)
|
||||
let n: Float = geometry_norm(g)
|
||||
let dn: Float = n - 5.0
|
||||
assert dn < 0.001, "3-4-5: norm is 5"
|
||||
assert dn > -0.001, "3-4-5: norm is 5"
|
||||
let nrm: Float = geometry_norm(g)
|
||||
let dnorm: Float = nrm - 5.0
|
||||
assert dnorm < 0.001, "3-4-5: norm is 5"
|
||||
assert dnorm > -0.001, "3-4-5: norm is 5"
|
||||
let freed: Int = geometry_free(g)
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Manifold — the corrected result of a transduction
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
test "a-manifold-is-a-value-that-holds-parts-and-relations" {
|
||||
let m: Manifold = manifold_new()
|
||||
let live: Int = manifold_is(m)
|
||||
assert live > 0, "manifold_new returns a live Manifold"
|
||||
let fresh_sz: Int = manifold_size(m)
|
||||
assert fresh_sz == 0, "a fresh manifold has no components"
|
||||
let fresh_rc: Int = manifold_rel_count(m)
|
||||
assert fresh_rc == 0, "a fresh manifold has no relations"
|
||||
let freed: Int = manifold_free(m)
|
||||
assert freed > 0, "manifold_free reports what it did"
|
||||
}
|
||||
|
||||
test "manifold-accessors-are-total" {
|
||||
let ni2: Int = manifold_is(0)
|
||||
assert ni2 < 1, "manifold_is of a non-manifold is 0"
|
||||
let ns: Int = manifold_size(0)
|
||||
assert ns < 1, "manifold_size of a non-manifold is 0"
|
||||
let nf2: Int = manifold_free(0)
|
||||
assert nf2 < 1, "manifold_free of a non-manifold is a no-op"
|
||||
let k: String = manifold_key(0, 0)
|
||||
assert str_eq(k, ""), "manifold_key of a non-manifold is empty, never a crash"
|
||||
}
|
||||
|
||||
test "components-are-addressed-by-key-not-by-index" {
|
||||
// The key is what survives persistence: a component becomes a node, and it
|
||||
// is separately groundable precisely because it is separately NAMED.
|
||||
let m: Manifold = manifold_new()
|
||||
let g: Geometry = geometry_new(1)
|
||||
let s: Int = geometry_set(g, 0, 7.0)
|
||||
let first_idx: Int = manifold_add(m, "rhythm", "temporal", g)
|
||||
assert first_idx == 0, "the first component is index 0"
|
||||
let found_idx: Int = manifold_index_of(m, "rhythm")
|
||||
assert found_idx == 0, "a component is found by its key"
|
||||
let missing: Int = manifold_index_of(m, "never_added")
|
||||
assert missing < 0, "an unknown key resolves to -1, not to component 0"
|
||||
let role: String = manifold_role(m, 0)
|
||||
assert str_eq(role, "temporal"), "a component carries what KIND of part it is"
|
||||
let f: Int = geometry_free(g)
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "a-duplicate-key-is-refused-because-addressing-must-be-unambiguous" {
|
||||
let m: Manifold = manifold_new()
|
||||
let g: Geometry = geometry_new(1)
|
||||
let ok_idx: Int = manifold_add(m, "pitch", "spectral", g)
|
||||
assert ok_idx == 0, "first add succeeds"
|
||||
let dup: Int = manifold_add(m, "pitch", "spectral", g)
|
||||
assert dup < 0, "two components answering to one name is not an addressing scheme"
|
||||
let dup_sz: Int = manifold_size(m)
|
||||
assert dup_sz == 1, "and the duplicate did not land"
|
||||
let f: Int = geometry_free(g)
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "a-part-with-no-geometry-is-not-a-part" {
|
||||
let m: Manifold = manifold_new()
|
||||
let bad: Int = manifold_add(m, "ghost", "none", 0)
|
||||
assert bad < 0, "a non-Geometry is refused as a component"
|
||||
let empty_key: Int = manifold_add(m, "", "none", geometry_new(1))
|
||||
assert empty_key < 0, "an unaddressable component is refused"
|
||||
let none_sz: Int = manifold_size(m)
|
||||
assert none_sz < 1, "nothing landed"
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "an-edge-to-a-nonexistent-endpoint-is-refused-not-dropped" {
|
||||
// A decomposition that silently loses edges is indistinguishable from one
|
||||
// that never had them.
|
||||
let m: Manifold = manifold_new()
|
||||
let g: Geometry = geometry_new(1)
|
||||
let a: Int = manifold_add(m, "here", "part", g)
|
||||
let dangling: Int = manifold_relate(m, "here", "points_at", "nowhere", 0.5)
|
||||
assert dangling < 1, "an edge to an unknown target is refused"
|
||||
let backwards: Int = manifold_relate(m, "nowhere", "points_at", "here", 0.5)
|
||||
assert backwards < 1, "an edge from an unknown source is refused"
|
||||
let dang_rc: Int = manifold_rel_count(m)
|
||||
assert dang_rc < 1, "and no relation was recorded"
|
||||
let f: Int = geometry_free(g)
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "a-component-owns-its-geometry-independently-of-the-caller" {
|
||||
// manifold_add COPIES. Freeing the caller's vector must not disturb the
|
||||
// component, or a decomposition would be unusable the moment it was built.
|
||||
let m: Manifold = manifold_new()
|
||||
let g: Geometry = geometry_new(2)
|
||||
let s0: Int = geometry_set(g, 0, 42.0)
|
||||
let idx: Int = manifold_add(m, "part", "kind", g)
|
||||
let freed: Int = geometry_free(g)
|
||||
assert freed > 0, "the caller freed its own vector"
|
||||
let back: Geometry = manifold_geometry(m, 0)
|
||||
let live: Int = geometry_is(back)
|
||||
assert live > 0, "the component still has geometry"
|
||||
let v: Float = geometry_get(back, 0)
|
||||
let dv: Float = v - 42.0
|
||||
assert dv < 0.001, "and it is the right geometry"
|
||||
assert dv > -0.001, "and it is the right geometry"
|
||||
let fb: Int = geometry_free(back)
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// transduce — signal in, SUBGRAPH out
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
test "a-realizer-declared-in-el-is-a-first-class-realizer" {
|
||||
// THE CLAIM, unchanged from #144: tone_realizer is an ordinary El function.
|
||||
// It is not in the runtime and the compiler knows nothing about it.
|
||||
// Registering it by name is enough to make it the organ for a modality.
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
assert reg > 0, "an El fn registers as a realizer by name"
|
||||
let has: Int = realizer_has("tone")
|
||||
assert has > 0, "the modality now has an organ"
|
||||
|
||||
let m: Manifold = transduce("CEG", "tone")
|
||||
let live: Int = manifold_is(m)
|
||||
assert live > 0, "transduce returns a real Manifold"
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "transduction-decomposes-a-signal-into-parts" {
|
||||
// THE CENTRAL CLAIM. "CEG" is three notes. What comes back is not one
|
||||
// vector standing for a chord — it is five addressable parts (three notes,
|
||||
// two intervals) and six relations. A fingerprint has one part by
|
||||
// construction and could not express this at any width.
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
let m: Manifold = transduce("CEG", "tone")
|
||||
|
||||
let ceg_sz: Int = manifold_size(m)
|
||||
assert ceg_sz == 5, "three notes and two intervals are five distinct parts"
|
||||
let ceg_rc: Int = manifold_rel_count(m)
|
||||
assert ceg_rc == 6, "and the parts stand in six stated relations"
|
||||
|
||||
// Every part is independently addressable BY NAME.
|
||||
let n0: Int = manifold_index_of(m, "note:0")
|
||||
assert n0 > -1, "the first note is addressable on its own"
|
||||
let n2: Int = manifold_index_of(m, "note:2")
|
||||
assert n2 > -1, "so is the third"
|
||||
let iv: Int = manifold_index_of(m, "interval:0-1")
|
||||
assert iv > -1, "so is the interval between the first two"
|
||||
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "each-part-carries-its-own-geometry" {
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
let m: Manifold = transduce("CEG", "tone")
|
||||
|
||||
// 'C' is 67. The note component's geometry is the note's, not the chord's.
|
||||
let note_i: Int = manifold_index_of(m, "note:0")
|
||||
let gn: Geometry = manifold_geometry(m, note_i)
|
||||
let note_dim: Int = geometry_dim(gn)
|
||||
assert note_dim == 2, "a note component has the width its realizer gave it"
|
||||
let pitch: Float = geometry_get(gn, 0)
|
||||
let dpitch: Float = pitch - 67.0
|
||||
assert dpitch < 0.001, "and it is C, so the signal reached the El realizer"
|
||||
assert dpitch > -0.001, "and it is C, so the signal reached the El realizer"
|
||||
|
||||
// Parts may have DIFFERENT widths. A single vector per signal cannot
|
||||
// represent parts of unequal dimensionality at all.
|
||||
let iv_i: Int = manifold_index_of(m, "interval:0-1")
|
||||
let gi: Geometry = manifold_geometry(m, iv_i)
|
||||
let iv_dim: Int = geometry_dim(gi)
|
||||
assert iv_dim == 1, "an interval component has its own, different width"
|
||||
|
||||
let f1: Int = geometry_free(gn)
|
||||
let f2: Int = geometry_free(gi)
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "the-relations-are-content-no-single-part-carries" {
|
||||
// THE POINT OF THE WHOLE CHANGE. C->E is two semitones. That "2" is not a
|
||||
// property of C and not a property of E; it exists only BETWEEN them. A
|
||||
// representation with no relations cannot hold it, which is why collapsing
|
||||
// a signal to one vector does not merely lose resolution — it loses a
|
||||
// category of content.
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
let m: Manifold = transduce("CEG", "tone")
|
||||
|
||||
let step_i: Int = manifold_index_of(m, "interval:0-1")
|
||||
let gi: Geometry = manifold_geometry(m, step_i)
|
||||
let step: Float = geometry_get(gi, 0)
|
||||
let dstep: Float = step - 2.0
|
||||
assert dstep < 0.001, "C to E is two semitones"
|
||||
assert dstep > -0.001, "C to E is two semitones"
|
||||
|
||||
// And the interval is WIRED to both endpoints, so the structure says which
|
||||
// two things it is the interval between.
|
||||
let spans: Int = 0
|
||||
let span_rc: Int = manifold_rel_count(m)
|
||||
let k: Int = 0
|
||||
while k < span_rc {
|
||||
let rn: String = manifold_rel_name(m, k)
|
||||
let rf: String = manifold_rel_from(m, k)
|
||||
if str_eq(rn, "spans") {
|
||||
if str_eq(rf, "interval:0-1") { spans = spans + 1 }
|
||||
}
|
||||
k = k + 1
|
||||
}
|
||||
assert spans == 2, "the interval is related to both notes it spans"
|
||||
|
||||
let fg: Int = geometry_free(gi)
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "relation-weight-is-the-grounding-carried-on-the-edge" {
|
||||
// correspondence-and-censorship.md §1: grounding is an attribute of the
|
||||
// edge and it IS the weight — one quantity, not a score computed beside
|
||||
// it. A realizer states a relation and its weight is the claim.
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
let m: Manifold = transduce("CE", "tone")
|
||||
|
||||
let ce_rc: Int = manifold_rel_count(m)
|
||||
assert ce_rc == 3, "one interval yields two spans and one ordering"
|
||||
|
||||
let found_w: Int = 0
|
||||
let k: Int = 0
|
||||
while k < ce_rc {
|
||||
let rn: String = manifold_rel_name(m, k)
|
||||
if str_eq(rn, "sounds_before") {
|
||||
let w: Float = manifold_rel_weight(m, k)
|
||||
let dw: Float = w - 0.8
|
||||
if dw < 0.001 { if dw > -0.001 { found_w = found_w + 1 } }
|
||||
}
|
||||
k = k + 1
|
||||
}
|
||||
assert found_w == 1, "the ordering relation carries the weight its realizer stated"
|
||||
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
test "distinct-signals-decompose-differently" {
|
||||
let reg: Int = realizer_register("tone", "tone_realizer")
|
||||
let m2: Manifold = transduce("CE", "tone")
|
||||
let m3: Manifold = transduce("CEG", "tone")
|
||||
let two_sz: Int = manifold_size(m2)
|
||||
let three_sz: Int = manifold_size(m3)
|
||||
assert two_sz == 3, "two notes decompose into two notes and one interval"
|
||||
assert three_sz == 5, "three notes decompose into three notes and two intervals"
|
||||
// Structure differs, not just position: fingerprints of a two-note and a
|
||||
// three-note signal have identical shape and differ only numerically.
|
||||
let two_rc: Int = manifold_rel_count(m2)
|
||||
let three_rc: Int = manifold_rel_count(m3)
|
||||
assert two_rc < three_rc, "and the relational structure itself differs"
|
||||
let f2: Int = manifold_free(m2)
|
||||
let f3: Int = manifold_free(m3)
|
||||
}
|
||||
|
||||
test "the-registry-keys-on-modality" {
|
||||
let r1: Int = realizer_register("tone", "tone_realizer")
|
||||
let rp: Int = realizer_register("pulse", "pulse_realizer")
|
||||
assert rp > 0, "a second modality registers independently"
|
||||
let mt: Manifold = transduce("CEG", "tone")
|
||||
let mp: Manifold = transduce("CEG", "pulse")
|
||||
let tone_sz: Int = manifold_size(mt)
|
||||
let pulse_sz: Int = manifold_size(mp)
|
||||
assert tone_sz == 5, "tone still routes to its own realizer"
|
||||
assert pulse_sz == 2, "pulse routes to a different realizer, with its own decomposition"
|
||||
let onset: Int = manifold_index_of(mp, "onset")
|
||||
assert onset > -1, "and to that realizer's own component vocabulary"
|
||||
let f1: Int = manifold_free(mt)
|
||||
let f2: Int = manifold_free(mp)
|
||||
}
|
||||
|
||||
test "no-organ-is-reported-as-no-organ" {
|
||||
// A modality with no realizer must transduce to NOTHING. It must never
|
||||
// fall back to embedding a description of the signal and calling that
|
||||
// perception — that silent substitution is the original defect.
|
||||
let has: Int = realizer_has("echolocation")
|
||||
assert has < 1, "unregistered modality has no organ"
|
||||
let m: Manifold = transduce("anything", "echolocation")
|
||||
let live: Int = manifold_is(m)
|
||||
assert live < 1, "no realizer means no manifold, not a fake one"
|
||||
}
|
||||
|
||||
test "registration-of-an-unresolvable-name-fails-loudly" {
|
||||
let bad: Int = realizer_register("ghost", "no_such_function_anywhere")
|
||||
assert bad < 1, "an unresolvable realizer name is a registration failure"
|
||||
let has: Int = realizer_has("ghost")
|
||||
assert has < 1, "and nothing gets registered"
|
||||
}
|
||||
|
||||
test "a-fingerprint-realizer-transduces-nothing" {
|
||||
// THE SUPERSESSION OF #144, asserted directly. fingerprint_realizer is
|
||||
// exactly what the merged primitive asked a realizer to be: signal in, one
|
||||
// Geometry out. It resolves, so registration succeeds — the organ is
|
||||
// present. But it does not decompose, so it does not transduce.
|
||||
//
|
||||
// This is a deliberate hard failure. "No organ" and "an organ that only
|
||||
// fingerprints" must not be indistinguishable, which is the same
|
||||
// distinction realizer_register already draws between an absent and a
|
||||
// broken organ. A modality with genuinely one part says so with
|
||||
// manifold_single, and is then visibly a size-1 manifold.
|
||||
let reg: Int = realizer_register("fingerprint", "fingerprint_realizer")
|
||||
assert reg > 0, "the symbol resolves, so registration succeeds"
|
||||
let m: Manifold = transduce("x", "fingerprint")
|
||||
let live: Int = manifold_is(m)
|
||||
assert live < 1, "a single vector is not a transduction"
|
||||
}
|
||||
|
||||
test "a-realizer-returning-nonsense-transduces-nothing" {
|
||||
let reg: Int = realizer_register("bogus", "bogus_realizer")
|
||||
assert reg > 0, "the symbol resolves, so registration succeeds"
|
||||
let m: Manifold = transduce("x", "bogus")
|
||||
let live: Int = manifold_is(m)
|
||||
assert live < 1, "a non-Manifold return transduced nothing"
|
||||
}
|
||||
|
||||
test "the-one-part-case-is-a-size-one-manifold-not-a-bare-vector" {
|
||||
// Some modalities really do have one part. That is a manifold of size 1 —
|
||||
// a special case of decomposition, not a parallel path back to a
|
||||
// fingerprint. Anything reading it still asks manifold_size and still gets
|
||||
// a real answer, and a second part can be added later without changing the
|
||||
// type of the thing.
|
||||
let g: Geometry = geometry_new(3)
|
||||
let s: Int = geometry_set(g, 0, 5.0)
|
||||
let m: Manifold = manifold_single("level", "scalar", g)
|
||||
let live: Int = manifold_is(m)
|
||||
assert live > 0, "manifold_single yields a real Manifold"
|
||||
let one_sz: Int = manifold_size(m)
|
||||
assert one_sz == 1, "of size one — visibly degenerate, not hidden"
|
||||
let idx: Int = manifold_index_of(m, "level")
|
||||
assert idx == 0, "and its one part is still addressable by name"
|
||||
let f: Int = geometry_free(g)
|
||||
let fm: Int = manifold_free(m)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user