diff --git a/crates/dr-math-binding-probe/Cargo.toml b/crates/dr-math-binding-probe/Cargo.toml new file mode 100644 index 000000000..4969346c0 --- /dev/null +++ b/crates/dr-math-binding-probe/Cargo.toml @@ -0,0 +1,12 @@ +# D-DRMC-1: isolated, non-production structural binding probe. +[package] +name = "dr-math-binding-probe" +version = "0.0.0" +edition = "2024" +publish = false +description = "Borrowed DR64 / ClassView-style typed structure and source witness admission probe; never executes population rows." + +[workspace] + +[dependencies] +lance-graph-contract = { path = "../lance-graph-contract" } diff --git a/crates/dr-math-binding-probe/README.md b/crates/dr-math-binding-probe/README.md new file mode 100644 index 000000000..67a671d24 --- /dev/null +++ b/crates/dr-math-binding-probe/README.md @@ -0,0 +1,18 @@ +# D-DRMC-1: DR64 typed borrow admission probe + +A **lab-only** carrier/binder seam next to Quack's existing developer-name-to-numeric resolution, not a replacement for `lance-graph-quack::bind::Binder`. Uses `lance_graph_contract::class_view::ClassId` and `morton8x8` directly; does not add types to the zero-dependency contract, modify the shared Quack binding code or implement another bulk executor. + +Run from the crate's directory (own `[workspace]`): `cargo test`. + +## Proved by these tests (once executed) + +- A DR64 register is borrowed as exactly `&[u64; 64]` (512 bytes), not copied or hydrated. +- `Identity×Identity` and `Identity×Property` are different **semantic method signatures** even with identical physical bits. +- Method ordinal matters within the same ClassView class. +- A three-valued method binds only with its validity bits, which travel WITH the relation bits (`Operand`); a two-valued method refuses validity; BAG is refused. No FALSE/UNKNOWN collapse and no lost multiplicity. +- Three guards, three types: the **frame** (source, relationship map, coordinates) is a store-issued seal compared by identity, so two stores at equal revisions do not alias and a re-seal is a new frame; the **focus** (motion) and the **scope** (policy) are separate guards that leave the frame valid when they move. Each moved revision is its own refusal. +- Morton 8×8 arithmetic addresses `(x,y)` precisely but does not establish relation truth. + +**Explicit non-proofs:** `FrameStore` is a lab stand-in for the source owner (the seal proves issue identity, not that the revisions describe real data); focus and scope are caller-supplied values, `MethodSignature` is a fixture rather than registry-resolved ClassView method metadata, no optimized fold or real foreign relation binder executes, no ACL policy is evaluated. Production admission requires the existing source owner, D-RMAP-1A table bindings, D-RFT/D-QSP method law, index seal and permission checks. A bitmap is not a universal row binding and cannot express all graph path multiplicities. + +Next stack: finite population claim/violation ledger, then compiler recipe eligibility. diff --git a/crates/dr-math-binding-probe/src/lib.rs b/crates/dr-math-binding-probe/src/lib.rs new file mode 100644 index 000000000..196afc4eb --- /dev/null +++ b/crates/dr-math-binding-probe/src/lib.rs @@ -0,0 +1,273 @@ +//! D-DRMC-1: borrowed typed DR64 operand admission, NOT an evaluator. +//! +//! This is a stand-alone probe of the ClassView -> Quack-style BIND contract. +//! It does not implement a ClassView registry, own RelationshipMap data, decide +//! mathematical equivalence, attest a source, or run any per-row operation. +//! Canonical code will remain in the existing owners; this lab tests the shape. +//! +//! The bitplane is precisely 64 rows of 64 participation bits (512 B). It +//! carries neither global ids, bag multiplicity nor provenance. A three-valued +//! method carries its validity bits WITH the relation bits (see [`Operand`]). +#![forbid(unsafe_code)] + +use std::sync::{Arc, Weak}; + +use lance_graph_contract::class_view::ClassId; + +/// What one numeric coordinate means according to the resolved ClassView. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum AxisKind { + Identity, + Property, + Community, + Capability, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Axis { + pub class: ClassId, + pub kind: AxisKind, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Truth { + TwoValued, + ThreeValued, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Population { + Distinct, + Bag, +} + +/// Per-method semantic law; *not* a single semantics choice for a whole class. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct MethodSignature { + pub owner_class: ClassId, + pub method_ordinal: u16, + pub row: Axis, + pub column: Axis, + pub truth: Truth, + pub population: Population, +} + +impl MethodSignature { + pub const fn presence( + owner_class: ClassId, + method_ordinal: u16, + row: Axis, + column: Axis, + ) -> Self { + Self { + owner_class, + method_ordinal, + row, + column, + truth: Truth::TwoValued, + population: Population::Distinct, + } + } +} + +/// The revisions that identify the reference: which source, which relation +/// map, which coordinate assignment. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct FrameRevisions { + pub source: u64, + pub relation_map: u64, + pub coordinates: u64, +} + +/// Lab stand-in for the owner of a frame. Only it issues [`Frame`]s, and a +/// frame compares by identity of that issue, never by value: two stores that +/// sit at the same numbers do not alias, and re-sealing equal numbers is a +/// new frame. The mechanism mirrors `dir-rmap-probe`'s `Witness` (a +/// `Weak` compared by `ptr_eq`); that type is tied to dir-sim's +/// `VersionStore`, so it is not reused here. +#[derive(Debug)] +pub struct FrameStore { + identity: Arc<()>, + current: Arc, +} + +impl FrameStore { + pub fn new(revisions: FrameRevisions) -> Self { + Self { + identity: Arc::new(()), + current: Arc::new(revisions), + } + } + + /// Seal the current frame. + pub fn seal(&self) -> Frame { + Frame { + store: Arc::downgrade(&self.identity), + seal: Arc::downgrade(&self.current), + at: *self.current, + } + } + + /// Move to new revisions; every frame sealed before is stale from now on. + pub fn advance(&mut self, revisions: FrameRevisions) { + self.current = Arc::new(revisions); + } + + pub fn revisions(&self) -> FrameRevisions { + *self.current + } + + /// `Ok` iff `frame` is this store's current seal. A frame of another store + /// is `ForeignFrame` even at equal numbers; a stale frame of this store + /// names the first revision that moved, or `FrameResealed` if none did. + pub fn check(&self, frame: &Frame) -> Result<(), Refusal> { + if !Weak::ptr_eq(&frame.store, &Arc::downgrade(&self.identity)) { + return Err(Refusal::ForeignFrame); + } + if Weak::ptr_eq(&frame.seal, &Arc::downgrade(&self.current)) { + return Ok(()); + } + let (then, now) = (frame.at, *self.current); + Err(if then.source != now.source { + Refusal::SourceChanged + } else if then.relation_map != now.relation_map { + Refusal::MapChanged + } else if then.coordinates != now.coordinates { + Refusal::CoordinatesChanged + } else { + Refusal::FrameResealed + }) + } +} + +/// A store-issued seal over the reference revisions. No public constructor: a +/// caller cannot build a frame from numbers, only receive one from +/// [`FrameStore::seal`]. +#[derive(Clone, Debug)] +pub struct Frame { + store: Weak<()>, + seal: Weak, + at: FrameRevisions, +} + +impl Frame { + /// The revisions this frame was sealed at (for reporting, never identity). + pub fn revisions(&self) -> FrameRevisions { + self.at + } + + /// Same issue of the same store. + pub fn same_issue(&self, other: &Frame) -> bool { + Weak::ptr_eq(&self.store, &other.store) && Weak::ptr_eq(&self.seal, &other.seal) + } +} + +/// Which 64x64 window of the frame is read. Motion, not reference: a new +/// focus over the same frame is not a new frame. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Focus { + pub revision: u64, +} + +/// What the reader is authorized to see. Policy, checked as its own guard at +/// borrow time; never part of frame identity. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Scope { + pub id: u64, +} + +/// What a binding was made under. +#[derive(Clone, Debug)] +pub struct Guards { + pub frame: Frame, + pub focus: Focus, + pub scope: Scope, +} + +/// The borrowed relation bits and, for a three-valued method, which of them +/// are known. Validity is a carrier OF R (one bit per R bit, like an Arrow +/// validity bitmap or Quack's `BoundField.validity`), never a separately typed +/// plane with a method of its own. +#[derive(Clone, Copy, Debug)] +pub struct Operand<'a> { + pub rows: &'a [u64; 64], + pub validity: Option<&'a [u64; 64]>, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Refusal { + MethodChanged, + AxisChanged, + SemanticsChanged, + ThreeValuedNeedsValidity, + TwoValuedHasNoValidity, + BagNeedsMultiplicity, + ForeignFrame, + SourceChanged, + MapChanged, + CoordinatesChanged, + FrameResealed, + FocusChanged, + ScopeChanged, +} + +/// Borrowed fixed-width register with its resolved type and guards. Never +/// owns the payload or reinterprets bits into foreign keys. +#[derive(Clone, Debug)] +pub struct BoundDr64<'a> { + pub method: MethodSignature, + pub frame: Frame, + pub focus: Focus, + pub scope: Scope, + pub rows: &'a [u64; 64], + pub validity: Option<&'a [u64; 64]>, +} + +/// Validate the declaration, then each guard on its own: the frame against +/// the store that issued it, then the focus, then the scope. Checks seal +/// identity and equality of focus and scope, not their authenticity; a +/// production binder also needs the ClassView registry, the tenant ACL and +/// the source owner. +pub fn bind_dr64<'a>( + store: &FrameStore, + declared: MethodSignature, + requested: MethodSignature, + bound_at: &Guards, + now: (Focus, Scope), + operand: Operand<'a>, +) -> Result, Refusal> { + if (declared.owner_class, declared.method_ordinal) + != (requested.owner_class, requested.method_ordinal) + { + return Err(Refusal::MethodChanged); + } + if (declared.row, declared.column) != (requested.row, requested.column) { + return Err(Refusal::AxisChanged); + } + if (declared.truth, declared.population) != (requested.truth, requested.population) { + return Err(Refusal::SemanticsChanged); + } + match (declared.truth, operand.validity) { + (Truth::ThreeValued, None) => return Err(Refusal::ThreeValuedNeedsValidity), + (Truth::TwoValued, Some(_)) => return Err(Refusal::TwoValuedHasNoValidity), + _ => {} + } + if declared.population != Population::Distinct { + return Err(Refusal::BagNeedsMultiplicity); + } + store.check(&bound_at.frame)?; + if bound_at.focus != now.0 { + return Err(Refusal::FocusChanged); + } + if bound_at.scope != now.1 { + return Err(Refusal::ScopeChanged); + } + Ok(BoundDr64 { + method: declared, + frame: bound_at.frame.clone(), + focus: now.0, + scope: now.1, + rows: operand.rows, + validity: operand.validity, + }) +} diff --git a/crates/dr-math-binding-probe/tests/binding.rs b/crates/dr-math-binding-probe/tests/binding.rs new file mode 100644 index 000000000..ef07254d2 --- /dev/null +++ b/crates/dr-math-binding-probe/tests/binding.rs @@ -0,0 +1,256 @@ +use dr_math_binding_probe::{ + Axis, AxisKind, Focus, FrameRevisions, FrameStore, Guards, MethodSignature, Operand, + Population, Refusal, Scope, Truth, bind_dr64, +}; +use lance_graph_contract::morton8x8::Morton8x8; + +fn id() -> Axis { + Axis { + class: 12, + kind: AxisKind::Identity, + } +} +fn property() -> Axis { + Axis { + class: 12, + kind: AxisKind::Property, + } +} +fn revisions() -> FrameRevisions { + FrameRevisions { + source: 7, + relation_map: 11, + coordinates: 3, + } +} +const FOCUS: Focus = Focus { revision: 5 }; +const SCOPE: Scope = Scope { id: 100 }; +fn guards(store: &FrameStore) -> Guards { + Guards { + frame: store.seal(), + focus: FOCUS, + scope: SCOPE, + } +} +fn signature() -> MethodSignature { + MethodSignature::presence(12, 1, id(), property()) +} +fn bits() -> [u64; 64] { + let mut rows = [0u64; 64]; + rows[17] |= 1u64 << 29; + rows +} +fn two_valued(rows: &[u64; 64]) -> Operand<'_> { + Operand { + rows, + validity: None, + } +} + +#[test] +fn bind_borrows_exact_bytes_no_materialization() { + let store = FrameStore::new(revisions()); + let rows = bits(); + let bound = bind_dr64( + &store, + signature(), + signature(), + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows), + ) + .unwrap(); + assert!(std::ptr::eq(bound.rows, &rows)); + assert_eq!(bound.rows[17], 1u64 << 29); + assert_eq!(std::mem::size_of_val(&rows), 512); +} + +#[test] +fn physical_equal_identity_squared_and_identity_property_are_not_interchangeable() { + let store = FrameStore::new(revisions()); + let rows = bits(); + let identity_squared = MethodSignature::presence(12, 1, id(), id()); + assert_eq!( + bind_dr64( + &store, + signature(), + identity_squared, + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), + Refusal::AxisChanged + ); +} + +#[test] +fn method_is_not_just_a_class_semantics() { + let store = FrameStore::new(revisions()); + let rows = bits(); + let other_method = MethodSignature::presence(12, 2, id(), property()); + assert_eq!( + bind_dr64( + &store, + signature(), + other_method, + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), + Refusal::MethodChanged + ); +} + +/// Validity travels with R and is typed by R's method: a three-valued method +/// needs it, a two-valued one must not carry it. There is no second method a +/// validity plane could be bound to, so a "validity of a foreign method" +/// cannot be written. +#[test] +fn validity_is_a_carrier_of_the_relation_typed_by_its_method() { + let store = FrameStore::new(revisions()); + let rows = bits(); + let known = bits(); + let mut k3 = signature(); + k3.truth = Truth::ThreeValued; + let bind = |m: MethodSignature, operand| { + bind_dr64(&store, m, m, &guards(&store), (FOCUS, SCOPE), operand) + }; + assert_eq!( + bind(k3, two_valued(&rows)).unwrap_err(), + Refusal::ThreeValuedNeedsValidity + ); + let with_validity = Operand { + rows: &rows, + validity: Some(&known), + }; + let bound = bind(k3, with_validity).unwrap(); + assert!(std::ptr::eq(bound.validity.unwrap(), &known)); + assert_eq!( + bind(signature(), with_validity).unwrap_err(), + Refusal::TwoValuedHasNoValidity + ); + let mut bag = signature(); + bag.population = Population::Bag; + assert_eq!( + bind(bag, two_valued(&rows)).unwrap_err(), + Refusal::BagNeedsMultiplicity + ); +} + +/// Each moved revision is its own refusal, and a re-seal at equal numbers is +/// still a new frame. +#[test] +fn every_frame_revision_is_a_separate_refusal() { + let rows = bits(); + let r = revisions(); + let cases = [ + (FrameRevisions { source: 8, ..r }, Refusal::SourceChanged), + ( + FrameRevisions { + relation_map: 12, + ..r + }, + Refusal::MapChanged, + ), + ( + FrameRevisions { + coordinates: 4, + ..r + }, + Refusal::CoordinatesChanged, + ), + (r, Refusal::FrameResealed), + ]; + for (moved, expected) in cases { + let mut store = FrameStore::new(r); + let bound_at = guards(&store); + store.advance(moved); + assert_eq!( + bind_dr64( + &store, + signature(), + signature(), + &bound_at, + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), + expected + ); + } +} + +/// The seal is store-issued identity, not equality of numbers: a frame of a +/// second store at the same revisions does not bind against the first. +#[test] +fn two_stores_at_equal_revisions_do_not_alias() { + let rows = bits(); + let first = FrameStore::new(revisions()); + let second = FrameStore::new(revisions()); + assert_eq!(first.revisions(), second.revisions()); + assert!(!first.seal().same_issue(&second.seal())); + assert!(first.seal().same_issue(&first.seal())); + assert_eq!( + bind_dr64( + &first, + signature(), + signature(), + &guards(&second), + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), + Refusal::ForeignFrame + ); +} + +/// Focus is motion and scope is policy: each is its own guard, and moving +/// either leaves the frame valid. +#[test] +fn focus_and_scope_are_guards_apart_from_the_frame() { + let store = FrameStore::new(revisions()); + let rows = bits(); + let bound_at = guards(&store); + let bind = |now| { + bind_dr64( + &store, + signature(), + signature(), + &bound_at, + now, + two_valued(&rows), + ) + }; + assert_eq!( + bind((Focus { revision: 6 }, SCOPE)).unwrap_err(), + Refusal::FocusChanged + ); + assert_eq!( + bind((FOCUS, Scope { id: 101 })).unwrap_err(), + Refusal::ScopeChanged + ); + assert_eq!(store.check(&bound_at.frame), Ok(())); + assert!(bind((FOCUS, SCOPE)).is_ok()); +} + +#[test] +fn cartesian_global_coordinate_is_not_a_foreign_relation_proof() { + let code = Morton8x8::from_xy(17, 29); + assert_eq!((code.x(), code.y()), (17, 29)); + // Cartesian addressability is exact; occupancy and semantics still + // require a DR relationship bit and a ClassView domain binding. + let store = FrameStore::new(revisions()); + let rows = bits(); + let b = bind_dr64( + &store, + signature(), + signature(), + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows), + ) + .unwrap(); + assert_eq!(b.rows[17] & (1u64 << 29), 1u64 << 29); +}