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
4 changes: 3 additions & 1 deletion src/bin/test_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ pub fn main() {
// println!("Queries: {:?}", random_queries);

for &n in &random_numbers {
features.push(SimpleNumberRecord::create(n.to_string()))
features.push(
SimpleNumberRecord::create(n.to_string()).expect("internally generated, always valid"),
)
}

let creation_start = Instant::now();
Expand Down
4 changes: 3 additions & 1 deletion src/bin/test_tlsh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ pub fn main() {
);
println!("Starting insertion into HNSW model...");
for f in dataset_copy {
apotheosis.insert(SimpleTlshRecord::create(f));
apotheosis.insert(
SimpleTlshRecord::create(f).expect("hash from output_hashes.json, always valid"),
);
}

let query_hash = TlshDefault::from_str(
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/apotheosis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ where
if let Some(ref key) = radix_key
&& self.radix.find(key).is_some()
{
println!("Key already exists in radix tree: {:?}", key);
tracing::warn!("Key already exists in radix tree: {:?}", key);
return false;
}

Expand Down
25 changes: 15 additions & 10 deletions src/datalayer/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,18 @@ pub type SimpleNumberRecord = SimpleRecord<u32>;
pub type SimpleTlshRecord = SimpleRecord<TlshDefault>;

impl SimpleNumberRecord {
pub fn create(s: String) -> Self {
let id = s.parse::<u32>().unwrap();
Self { id, radix_key: s }
pub fn create(s: String) -> Result<Self, Box<dyn std::error::Error>> {
let id = s
.parse::<u32>()
.map_err(|e| format!("invalid number record {s:?}: {e}"))?;
Ok(Self { id, radix_key: s })
}
}

impl SimpleTlshRecord {
pub fn create(s: String) -> Self {
let id = TlshDefault::from_str(&s).unwrap();
Self { id, radix_key: s }
pub fn create(s: String) -> Result<Self, Box<dyn std::error::Error>> {
let id = TlshDefault::from_str(&s).map_err(|_| format!("invalid TLSH hash {s:?}"))?;
Ok(Self { id, radix_key: s })
}
}

Expand Down Expand Up @@ -99,13 +101,16 @@ impl ApotheosisRecord for GenericJsonRecord {
}

impl GenericJsonRecord {
pub fn create(s: String, metadata: serde_json::Value) -> Self {
let id = TlshDefault::from_str(&s).unwrap();
Self {
pub fn create(
s: String,
metadata: serde_json::Value,
) -> Result<Self, Box<dyn std::error::Error>> {
let id = TlshDefault::from_str(&s).map_err(|_| format!("invalid TLSH hash {s:?}"))?;
Ok(Self {
id,
radix_key: s,
metadata,
}
})
}
}

Expand Down
8 changes: 4 additions & 4 deletions tests/ann_correctness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn ann_recall_matches_brute_force() {
let mut inserted = vec![];
for i in 0..300u32 {
let value = i * 2;
idx.insert(SimpleNumberRecord::create(value.to_string()));
idx.insert(SimpleNumberRecord::create(value.to_string()).unwrap());
inserted.push(value);
}

Expand Down Expand Up @@ -94,7 +94,7 @@ fn multi_layer_search_correct_across_layers() {
let mut inserted = vec![];
for i in 0..300u32 {
let value = i * 3;
idx.insert(SimpleNumberRecord::create(value.to_string()));
idx.insert(SimpleNumberRecord::create(value.to_string()).unwrap());
inserted.push(value);
}

Expand Down Expand Up @@ -160,8 +160,8 @@ fn heuristic_produces_different_graph_than_greedy() {
let mut heuristic = ApoNumHeuristic::new();

for i in 0..n {
greedy.insert(SimpleNumberRecord::create(i.to_string())); // safe: sequential, unique
heuristic.insert(SimpleNumberRecord::create(i.to_string())); // safe: sequential, unique
greedy.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: sequential, unique
heuristic.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: sequential, unique
}

// Collect edge sets as sorted (src, dst) string pairs - direction-agnostic.
Expand Down
12 changes: 6 additions & 6 deletions tests/api_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type ApoNum = Apotheosis<SimpleNumberRecord, NormalDistance>;
fn search_ann_path_returns_k_or_fewer_sorted_by_distance() {
let mut idx = ApoNum::new();
for i in 0..20u32 {
idx.insert(SimpleNumberRecord::create(i.to_string()));
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}

let k = 5;
Expand Down Expand Up @@ -72,7 +72,7 @@ fn draw_produces_one_gexf_file_per_layer() {

let mut idx = ApoNum::new();
for i in 0..200u32 {
idx.insert(SimpleNumberRecord::create(i.to_string()));
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}

let layer_count = idx.draw_model().len();
Expand Down Expand Up @@ -110,13 +110,13 @@ fn load_overwrites_previous_in_memory_state() {

let mut idx_a = ApoNum::new();
for i in 0..5u32 {
idx_a.insert(SimpleNumberRecord::create(i.to_string()));
idx_a.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}
idx_a.dump(path).expect("dump() must not fail");

let mut idx_b = ApoNum::new();
for i in 100..103u32 {
idx_b.insert(SimpleNumberRecord::create(i.to_string()));
idx_b.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}
idx_b = ApoNum::load(path).expect("load() must not fail");

Expand Down Expand Up @@ -202,7 +202,7 @@ fn load_rejects_truncated_file() {
fn search_with_k_zero_returns_empty() {
let mut idx = ApoNum::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: unique keys
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: unique keys
}
// Query not in dataset → ANN path → .take(0) → empty Vec.
let results = idx.search(&9999u32, 0, None);
Expand All @@ -223,7 +223,7 @@ fn search_with_k_zero_returns_empty() {
fn search_with_k_greater_than_n_returns_all() {
let mut idx = ApoNum::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: unique keys
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: unique keys
}
let k = 100; // far larger than the 10 records
let results = idx.search(&9999u32, k, None);
Expand Down
22 changes: 11 additions & 11 deletions tests/config_boundary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type ApoNumSmallSat = Apotheosis<SimpleNumberRecord, NormalDistance, 4, 8, 100>;
fn sync_invariant_min_viable() {
let mut idx = ApoMinViable::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let zero_len = idx.draw_model()[0].0.len();
assert_eq!(idx.len(), 10);
Expand All @@ -58,7 +58,7 @@ fn sync_invariant_min_viable() {
fn sync_invariant_inverted_ratio() {
let mut idx = ApoInverted::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let zero_len = idx.draw_model()[0].0.len();
assert_eq!(idx.len(), 10);
Expand All @@ -70,7 +70,7 @@ fn sync_invariant_inverted_ratio() {
fn sync_invariant_equal_m_m0() {
let mut idx = ApoEqualMM0::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let zero_len = idx.draw_model()[0].0.len();
assert_eq!(idx.len(), 10);
Expand All @@ -82,7 +82,7 @@ fn sync_invariant_equal_m_m0() {
fn sync_invariant_dense() {
let mut idx = ApoDense::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let zero_len = idx.draw_model()[0].0.len();
assert_eq!(idx.len(), 10);
Expand All @@ -102,7 +102,7 @@ fn sync_invariant_dense() {
fn search_no_panic_min_viable() {
let mut idx = ApoMinViable::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let k = 3;
// Query not in dataset → ANN path. ef_search=None → unwrap_or(24) inside search().
Expand All @@ -122,7 +122,7 @@ fn search_no_panic_min_viable() {
fn search_no_panic_inverted_ratio() {
let mut idx = ApoInverted::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let k = 3;
let results = idx.search(&9999u32, k, None);
Expand All @@ -141,7 +141,7 @@ fn search_no_panic_inverted_ratio() {
fn search_no_panic_equal_m_m0() {
let mut idx = ApoEqualMM0::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let k = 3;
let results = idx.search(&9999u32, k, None);
Expand All @@ -160,7 +160,7 @@ fn search_no_panic_equal_m_m0() {
fn search_no_panic_dense() {
let mut idx = ApoDense::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let k = 3;
let results = idx.search(&9999u32, k, None);
Expand Down Expand Up @@ -188,7 +188,7 @@ fn search_no_panic_dense() {
fn search_ef_search_greater_than_n() {
let mut idx = ApoDefault::new();
for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let k = 5;
let results = idx.search(&9999u32, k, Some(1000));
Expand All @@ -209,7 +209,7 @@ fn search_ef_search_greater_than_n() {
fn search_k_greater_than_ef_search() {
let mut idx = ApoDefault::new();
for i in 0..20u32 {
idx.insert(SimpleNumberRecord::create(i.to_string())); // safe: keys are unique
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()); // safe: keys are unique
}
let k = 10;
let results = idx.search(&9999u32, k, Some(2));
Expand Down Expand Up @@ -241,7 +241,7 @@ fn eviction_under_saturation_keeps_graph_consistent() {
let mut idx = ApoNumSmallSat::new();
let n = 300u32;
for i in 0..n {
idx.insert(SimpleNumberRecord::create(i.to_string()));
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}

// Every key must still resolve exactly through the radix fast-path.
Expand Down
6 changes: 3 additions & 3 deletions tests/config_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn build_dataset_default() -> ApoNumDefault {
let mut idx = ApoNumDefault::new();
for i in 0..10u32 {
assert!(
idx.insert(SimpleNumberRecord::create(i.to_string())),
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()),
"insert unexpectedly returned false for key {i}"
);
}
Expand All @@ -34,7 +34,7 @@ fn build_dataset_small() -> ApoNumSmall {
let mut idx = ApoNumSmall::new();
for i in 0..10u32 {
assert!(
idx.insert(SimpleNumberRecord::create(i.to_string())),
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()),
"insert unexpectedly returned false for key {i}"
);
}
Expand All @@ -45,7 +45,7 @@ fn build_dataset_heuristic() -> ApoNumHeuristic {
let mut idx = ApoNumHeuristic::new();
for i in 0..10u32 {
assert!(
idx.insert(SimpleNumberRecord::create(i.to_string())),
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap()),
"insert unexpectedly returned false for key {i}"
);
}
Expand Down
20 changes: 10 additions & 10 deletions tests/fitness_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn sync_invariant_holds_after_bulk_insert() {
let mut idx = ApoNum::new();

for i in 0..50u32 {
idx.insert(SimpleNumberRecord::create(i.to_string()));
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}

let hnsw_len = idx.draw_model()[0].0.len();
Expand Down Expand Up @@ -59,7 +59,7 @@ fn sync_invariant_holds_after_bulk_insert() {
#[test]
fn insert_returns_true_for_new_keys() {
let mut idx = ApoNum::new();
let result = idx.insert(SimpleNumberRecord::create("1".to_string()));
let result = idx.insert(SimpleNumberRecord::create("1".to_string()).unwrap());
assert!(result, "insert() must return true for a new unique key");
}

Expand All @@ -79,10 +79,10 @@ fn insert_returns_true_for_new_keys() {
fn insert_returns_false_for_duplicate_keys() {
let mut idx = ApoNum::new();

let first = idx.insert(SimpleNumberRecord::create("42".to_string()));
let first = idx.insert(SimpleNumberRecord::create("42".to_string()).unwrap());
assert!(first, "first insert of key '42' must return true");

let second = idx.insert(SimpleNumberRecord::create("42".to_string()));
let second = idx.insert(SimpleNumberRecord::create("42".to_string()).unwrap());
assert!(!second, "duplicate insert of key '42' must return false");
}

Expand All @@ -102,7 +102,7 @@ fn insert_returns_false_for_duplicate_keys() {
#[test]
fn search_returns_exact_match_via_fast_path() {
let mut idx = ApoNum::new();
idx.insert(SimpleNumberRecord::create("100".to_string()));
idx.insert(SimpleNumberRecord::create("100".to_string()).unwrap());

let query: u32 = 100;
let results = idx.search(&query, 1, None);
Expand Down Expand Up @@ -130,7 +130,7 @@ fn search_returns_k_or_fewer_results() {
let mut idx = ApoNum::new();

for i in 0..10u32 {
idx.insert(SimpleNumberRecord::create(i.to_string()));
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}

let query: u32 = 5;
Expand Down Expand Up @@ -163,7 +163,7 @@ fn dump_load_roundtrip_preserves_search_results() {

let mut idx = ApoNum::new();
for i in 0..20u32 {
idx.insert(SimpleNumberRecord::create(i.to_string()));
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}

let query: u32 = 7;
Expand Down Expand Up @@ -205,10 +205,10 @@ fn dump_load_roundtrip_preserves_search_results() {
fn sync_invariant_holds_after_duplicate_insert() {
let mut idx = ApoNum::new();

let first = idx.insert(SimpleNumberRecord::create("7".to_string()));
let first = idx.insert(SimpleNumberRecord::create("7".to_string()).unwrap());
assert!(first, "first insert must return true");

let second = idx.insert(SimpleNumberRecord::create("7".to_string()));
let second = idx.insert(SimpleNumberRecord::create("7".to_string()).unwrap());
assert!(!second, "duplicate insert must return false");

let zero_layer_count = idx.draw_model()[0].0.len();
Expand Down Expand Up @@ -239,7 +239,7 @@ fn sync_invariant_maps_every_key_to_its_own_record() {
let mut idx = ApoNum::new();
let n = 100u32;
for i in 0..n {
idx.insert(SimpleNumberRecord::create(i.to_string()));
idx.insert(SimpleNumberRecord::create(i.to_string()).unwrap());
}

for key in 0..n {
Expand Down
Loading
Loading