Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions crates/cognitive-shader-driver/src/bindspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ pub struct BindSpace {
pub meta: MetaColumn,
pub temporal: Box<[u64]>,
pub expert: Box<[u16]>,
/// Column H: per-row entity type binding (Foundry Object Type equivalent).
/// 0 = untyped. Non-zero = 1-based index into `Ontology.schemas`.
pub entity_type: Box<[u16]>,
}

impl BindSpace {
Expand All @@ -157,6 +160,7 @@ impl BindSpace {
meta: MetaColumn::zeros(len),
temporal: vec![0u64; len].into_boxed_slice(),
expert: vec![0u16; len].into_boxed_slice(),
entity_type: vec![0u16; len].into_boxed_slice(),
}
}

Expand All @@ -169,7 +173,8 @@ impl BindSpace {
let meta_bytes = self.len * 4;
let temporal_bytes = self.len * 8;
let expert_bytes = self.len * 2;
content_topic_angle + cycle_bytes + edge_bytes + qualia_bytes + meta_bytes + temporal_bytes + expert_bytes
let entity_type_bytes = self.len * 2;
content_topic_angle + cycle_bytes + edge_bytes + qualia_bytes + meta_bytes + temporal_bytes + expert_bytes + entity_type_bytes
}

/// Apply MetaFilter across a row window. Returns a dense Vec of row
Expand Down Expand Up @@ -220,6 +225,20 @@ impl BindSpaceBuilder {
qualia: &[f32; QUALIA_DIMS],
temporal: u64,
expert: u16,
) -> Self {
self.push_typed(content, meta, edge, qualia, temporal, expert, 0)
}

/// Push a row with explicit entity type (Column H).
pub fn push_typed(
mut self,
content: &[u64],
meta: MetaWord,
edge: u64,
qualia: &[f32; QUALIA_DIMS],
temporal: u64,
expert: u16,
entity_type: u16,
) -> Self {
let row = self.cursor;
self.bs.fingerprints.set_content(row, content);
Expand All @@ -228,6 +247,7 @@ impl BindSpaceBuilder {
self.bs.qualia.set(row, qualia);
self.bs.temporal[row] = temporal;
self.bs.expert[row] = expert;
self.bs.entity_type[row] = entity_type;
self.cursor += 1;
self
}
Expand All @@ -254,9 +274,9 @@ mod tests {
#[test]
fn bindspace_footprint_adds_columns() {
let bs = BindSpace::zeros(1);
// 3 × 2048 (content/topic/angle) + 65536 (cycle f32) + 8 (edge) + 72 (qualia 18×4) + 4 (meta) + 8 (temporal) + 2 (expert)
// = 6144 + 65536 + 8 + 72 + 4 + 8 + 2 = 71774
assert_eq!(bs.byte_footprint(), 71774);
// 3 × 2048 (content/topic/angle) + 65536 (cycle f32) + 8 (edge) + 72 (qualia 18×4) + 4 (meta) + 8 (temporal) + 2 (expert) + 2 (entity_type)
// = 6144 + 65536 + 8 + 72 + 4 + 8 + 2 + 2 = 71776
assert_eq!(bs.byte_footprint(), 71776);
}

#[test]
Expand Down Expand Up @@ -298,6 +318,37 @@ mod tests {
assert!(bs.fingerprints.cycle_row(0).iter().all(|&v| v == 0.0));
}

#[test]
fn entity_type_defaults_to_untyped() {
let bs = BindSpace::zeros(4);
for row in 0..4 {
assert_eq!(bs.entity_type[row], 0, "default should be untyped (0)");
}
}

#[test]
fn entity_type_set_and_get() {
let mut bs = BindSpace::zeros(4);
bs.entity_type[1] = 42;
bs.entity_type[3] = 7;
assert_eq!(bs.entity_type[0], 0);
assert_eq!(bs.entity_type[1], 42);
assert_eq!(bs.entity_type[2], 0);
assert_eq!(bs.entity_type[3], 7);
}

#[test]
fn builder_push_typed_sets_entity_type() {
let qualia = [0.0f32; QUALIA_DIMS];
let content = [0u64; WORDS_PER_FP];
let bs = BindSpaceBuilder::new(2)
.push_typed(&content, MetaWord::new(1, 0, 100, 100, 0), 0, &qualia, 0, 0, 5)
.push(&content, MetaWord::new(2, 0, 200, 200, 0), 0, &qualia, 0, 0)
.build();
assert_eq!(bs.entity_type[0], 5, "push_typed should set entity_type");
assert_eq!(bs.entity_type[1], 0, "push should default to 0");
}

#[test]
fn set_cycle_direct_f32() {
let mut bs = BindSpace::zeros(2);
Expand Down
40 changes: 40 additions & 0 deletions crates/lance-graph-contract/src/ontology.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ use crate::property::{
};
use crate::cam::CodecRoute;

// ═══════════════════════════════════════════════════════════════════════════
// EntityTypeId — Foundry Object Type equivalent (Column H in BindSpace SoA)
// ═══════════════════════════════════════════════════════════════════════════

/// Numeric entity type identifier for per-row BindSpace typing.
/// 0 = untyped. Non-zero = index into `Ontology.schemas` (1-based).
///
/// This is the Palantir Vertex "Object Type" equivalent. Every row in
/// BindSpace can be typed, enabling Object Explorer scrolling, property
/// view selection (LF-22 ObjectView), and type-filtered search (LF-40).
pub type EntityTypeId = u16;

/// Look up the EntityTypeId for a named entity type within an Ontology.
/// Returns 0 if the name doesn't match any schema.
pub fn entity_type_id(ontology: &Ontology, name: &str) -> EntityTypeId {
ontology.schemas.iter()
.position(|s| s.name == name)
.map(|idx| (idx + 1) as EntityTypeId)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent EntityTypeId wraparound on large ontologies

Casting (idx + 1) to u16 here will silently wrap once ontology.schemas.len() >= 65_536, which can turn a real schema into 0 (the reserved “untyped” sentinel) or collide with another type ID. In those large-ontology cases this corrupts row typing semantics in BindSpace and makes type-based filtering unreliable; this lookup should detect overflow and fail explicitly (or otherwise enforce the maximum schema count) instead of truncating.

Useful? React with 👍 / 👎.

.unwrap_or(0)
}

// ═══════════════════════════════════════════════════════════════════════════
// Ontology — the composed object model
// ═══════════════════════════════════════════════════════════════════════════
Expand Down Expand Up @@ -371,4 +392,23 @@ mod tests {
assert!(PrefetchDepth::Detail < PrefetchDepth::Similar);
assert!(PrefetchDepth::Similar < PrefetchDepth::Full);
}

#[test]
fn entity_type_id_returns_1_based_index() {
use crate::property::Schema;
let ont = Ontology {
name: "test",
schemas: vec![
Schema::builder("Customer").build(),
Schema::builder("Invoice").build(),
Schema::builder("Product").build(),
],
links: Vec::new(),
actions: Vec::new(),
};
assert_eq!(entity_type_id(&ont, "Customer"), 1);
assert_eq!(entity_type_id(&ont, "Invoice"), 2);
assert_eq!(entity_type_id(&ont, "Product"), 3);
assert_eq!(entity_type_id(&ont, "Unknown"), 0);
}
}
Loading