Skip to content

Commit ac2f15a

Browse files
committed
Close exp 9 measurement gap: traced search variants, real coherence
Two new OMC builtins return [result, probe_indices_array]: phi_pi_fib_search_traced(arr, target) -> [int, array] phi_pi_fib_nearest_traced(arr, target) -> [int, array] Backed by a new fibonacci_search_with_trace in phi_pi_fib.rs that collects probed indices alongside the normal search and shares the global compare counters. Re-running experiment 9 with traced phi_pi_fib gives REAL coherence numbers instead of the prior 0/0 measurement gap: linear scan: step coherence 2800/2800 = 1.000 (degenerate) binary search: step coherence 392/592 = 0.662 (~34% NOT Fibonacci) phi_pi_fib: step coherence 821/916 = 0.896 (~90% ARE Fibonacci) The phi_pi_fib trace for phi.fold(50) is now visible: probes [14, 13, 8, 12, 11, 10, 10] step sizes [1, 5, 4, 1, 1, 0] — 5 of 6 are Fibonacci, the lone outlier (4) comes from an offset-reset after a "go right" branch. So phi_pi_fib is empirically ~90% substrate-coherent, not 100% as I analytically claimed. Binary's apparent 66% is partly chance — small-attractor tables happen to have Fibonacci-valued midpoint step sizes (1, 2, 3, 8); at larger tables binary's halving step sizes (16, 32, 64, 128) are all non-Fibonacci, so its coherence should DROP while phi_pi_fib's stays high. Path is now: phi_pi_fib is the substrate-coherent choice for the foundational phi.fold and HInt::compute_resonance operations. Next commit does the refactor — promote phi_pi_fib::FIBONACCI to canonical, delete the 17 duplicate fibs tables, rewrite fold_to_fibonacci_const and HInt::compute_resonance to use phi_pi_fib_nearest. Both engines audit byte-identical (3114 bytes). 148/148 tests pass.
1 parent aa4bd51 commit ac2f15a

3 files changed

Lines changed: 153 additions & 18 deletions

File tree

experiments/hybrid_llm/experiment_9_phi_fold_alignment.omc

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,19 @@ fn phi_fold_binary(x) -> array {
129129
}
130130

131131
# Variant C: Fibonacci-step search via the substrate's own algorithm.
132-
# We use phi_pi_fib_search but ALSO track compares via the shared counter.
132+
# Uses the traced builtin so we can measure step-size coherence
133+
# externally — this closes the measurement gap from the v1 of this
134+
# experiment.
133135
fn phi_fold_fibstep(x) -> array {
134136
h abs_x = x;
135137
if abs_x < 0 { abs_x = 0 - abs_x; }
136138
phi_pi_fib_reset();
137-
h idx = phi_pi_fib_nearest(FIBS, abs_x);
139+
h traced = phi_pi_fib_nearest_traced(FIBS, abs_x);
140+
h idx = arr_get(traced, 0);
141+
h probes = arr_get(traced, 1);
138142
h s = phi_pi_fib_stats();
139143
h compares = arr_get(s, 1);
140144
h nearest = arr_get(FIBS, idx);
141-
# We can't recover the probe trace from the builtin directly.
142-
# Instead, emit the index returned + a synthetic trace marker.
143-
h probes = [];
144-
arr_push(probes, idx);
145145
h out = arr_new(3, 0);
146146
arr_set(out, 0, nearest);
147147
arr_set(out, 1, compares);
@@ -286,18 +286,10 @@ report("linear scan: ", agg_lin);
286286
report("binary search:", agg_bin);
287287
report("phi_pi_fib: ", agg_fib);
288288
print("");
289-
print("NOTE on phi_pi_fib coherence: 0/0 above is a MEASUREMENT GAP,");
290-
print("not a real zero. The builtin phi_pi_fib_nearest doesn't expose");
291-
print("its internal probe trace, so we can't externally tally its step");
292-
print("sizes. Analytically, the Rust implementation in phi_pi_fib.rs");
293-
print("decrements fib_idx by 1 or 2 each iteration and probes at");
294-
print("offset + F(fib_idx); step sizes between consecutive probes are");
295-
print("F(k) values, which ARE Fibonacci by construction. So the");
296-
print("coherence is ~1.0 by design even though this experiment can't");
297-
print("measure it directly. Fixing the gap requires either adding a");
298-
print("traced variant to the builtin surface, or porting the search");
299-
print("to OMC for tracing. Worthwhile follow-up; the compare-count");
300-
print("savings (and the architectural argument) hold either way.");
289+
print("Coherence is now measured directly for phi_pi_fib using the");
290+
print("phi_pi_fib_nearest_traced builtin (new in this commit). Each");
291+
print("call returns [nearest_index, probes_array]; step coherence is");
292+
print("computed on consecutive elements of probes_array.");
301293
print("");
302294

303295
# ---------------------------------------------------------------------------

omnimcode-core/src/interpreter.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,8 @@ impl Interpreter {
16661666
// Phi-Pi-Fib search v2 + binary baseline + theoretical bound
16671667
| "phi_pi_fib_search_v2" | "phi_pi_fib_nearest_v2"
16681668
| "phi_pi_bin_search" | "log_phi_pi_fibonacci"
1669+
// Traced variants — return [result, probe_indices_array]
1670+
| "phi_pi_fib_search_traced" | "phi_pi_fib_nearest_traced"
16691671
// Self-healing
16701672
| "safe_divide" | "safe_arr_get" | "safe_arr_set"
16711673
| "safe_add" | "safe_sub" | "safe_mul" | "resolve_singularity"
@@ -4133,6 +4135,96 @@ impl Interpreter {
41334135
let n = self.eval_expr(&args[0])?.to_float();
41344136
Ok(Value::HFloat(crate::phi_pi_fib::log_phi_pi_fibonacci(n)))
41354137
}
4138+
// phi_pi_fib_search_traced(sorted_arr, target)
4139+
// Returns [result_int, probe_indices_array]. `result_int`
4140+
// is the exact-match index when found, or -(insert_pos+1)
4141+
// when not. `probe_indices_array` is the sequence of
4142+
// indices the Fibonacci-step search visited, in order.
4143+
// Used by experiments that need to measure step-size
4144+
// coherence externally.
4145+
"phi_pi_fib_search_traced" => {
4146+
if args.len() < 2 {
4147+
return Err("phi_pi_fib_search_traced requires (sorted_array, target)".to_string());
4148+
}
4149+
let arr_v = self.eval_expr(&args[0])?;
4150+
let target = self.eval_expr(&args[1])?.to_int();
4151+
if let Value::Array(arr) = arr_v {
4152+
let items_b = arr.items.borrow();
4153+
let ints: Vec<i64> = items_b.iter().map(|v| v.to_int()).collect();
4154+
let (r, probes) = crate::phi_pi_fib::fibonacci_search_with_trace(
4155+
&ints,
4156+
&target,
4157+
|a, b| if a < b { -1 } else if a > b { 1 } else { 0 },
4158+
);
4159+
let result_int = match r {
4160+
Ok(i) => i as i64,
4161+
Err(insert_pos) => -(insert_pos as i64 + 1),
4162+
};
4163+
let probe_vals: Vec<Value> = probes
4164+
.into_iter()
4165+
.map(|p| Value::HInt(HInt::new(p as i64)))
4166+
.collect();
4167+
let out = vec![
4168+
Value::HInt(HInt::new(result_int)),
4169+
Value::Array(HArray::from_vec(probe_vals)),
4170+
];
4171+
Ok(Value::Array(HArray::from_vec(out)))
4172+
} else {
4173+
Err("phi_pi_fib_search_traced: first argument must be an array".to_string())
4174+
}
4175+
}
4176+
// phi_pi_fib_nearest_traced(sorted_arr, target)
4177+
// Returns [nearest_index, probe_indices_array]. Always
4178+
// resolves to a valid nearest index (or -1 for empty arrays).
4179+
"phi_pi_fib_nearest_traced" => {
4180+
if args.len() < 2 {
4181+
return Err("phi_pi_fib_nearest_traced requires (sorted_array, target)".to_string());
4182+
}
4183+
let arr_v = self.eval_expr(&args[0])?;
4184+
let target = self.eval_expr(&args[1])?.to_int();
4185+
if let Value::Array(arr) = arr_v {
4186+
let items_b = arr.items.borrow();
4187+
let ints: Vec<i64> = items_b.iter().map(|v| v.to_int()).collect();
4188+
if ints.is_empty() {
4189+
let out = vec![
4190+
Value::HInt(HInt::new(-1)),
4191+
Value::Array(HArray::from_vec(vec![])),
4192+
];
4193+
return Ok(Value::Array(HArray::from_vec(out)));
4194+
}
4195+
let (r, probes) = crate::phi_pi_fib::fibonacci_search_with_trace(
4196+
&ints,
4197+
&target,
4198+
|a, b| if a < b { -1 } else if a > b { 1 } else { 0 },
4199+
);
4200+
let idx: usize = match r {
4201+
Ok(i) => i,
4202+
Err(insert_pos) => {
4203+
let n = ints.len();
4204+
if insert_pos == 0 {
4205+
0
4206+
} else if insert_pos >= n {
4207+
n - 1
4208+
} else {
4209+
let left = (target - ints[insert_pos - 1]).abs();
4210+
let right = (ints[insert_pos] - target).abs();
4211+
if right < left { insert_pos } else { insert_pos - 1 }
4212+
}
4213+
}
4214+
};
4215+
let probe_vals: Vec<Value> = probes
4216+
.into_iter()
4217+
.map(|p| Value::HInt(HInt::new(p as i64)))
4218+
.collect();
4219+
let out = vec![
4220+
Value::HInt(HInt::new(idx as i64)),
4221+
Value::Array(HArray::from_vec(probe_vals)),
4222+
];
4223+
Ok(Value::Array(HArray::from_vec(out)))
4224+
} else {
4225+
Err("phi_pi_fib_nearest_traced: first argument must be an array".to_string())
4226+
}
4227+
}
41364228
"arr_slice" => {
41374229
if args.len() < 3 {
41384230
return Err("arr_slice requires (array, start, end)".to_string());
@@ -5102,6 +5194,7 @@ pub(crate) const HEAL_BUILTIN_NAMES: &[&str] = &[
51025194
"phi_pi_fib_stats", "phi_pi_fib_reset",
51035195
"phi_pi_fib_search_v2", "phi_pi_fib_nearest_v2",
51045196
"phi_pi_bin_search", "log_phi_pi_fibonacci",
5197+
"phi_pi_fib_search_traced", "phi_pi_fib_nearest_traced",
51055198
// Self-healing
51065199
"safe_divide", "safe_arr_get", "safe_arr_set",
51075200
"safe_add", "safe_sub", "safe_mul", "resolve_singularity",

omnimcode-core/src/phi_pi_fib.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,56 @@ pub fn phi_pi_fib_search_v2<T>(
225225
Err(low)
226226
}
227227

228+
/// fibonacci_search_with_trace — same as fibonacci_search but also
229+
/// returns the sequence of probed indices, in order. Used by
230+
/// experiments that need to measure step-size coherence externally.
231+
/// Counters are updated identically to fibonacci_search so combined
232+
/// runs still report meaningful totals.
233+
pub fn fibonacci_search_with_trace<T>(
234+
arr: &[T],
235+
target: &T,
236+
cmp: impl Fn(&T, &T) -> i32,
237+
) -> (Result<usize, usize>, Vec<usize>) {
238+
let mut probes: Vec<usize> = Vec::new();
239+
if arr.is_empty() {
240+
return (Err(0), probes);
241+
}
242+
TOTAL_SEARCHES.fetch_add(1, Ordering::Relaxed);
243+
244+
let mut fib_idx = find_fib_index(arr.len());
245+
let mut offset = 0usize;
246+
let mut comparisons = 0u64;
247+
248+
while fib_idx > 0 {
249+
comparisons += 1;
250+
let fib_val = get_fib(fib_idx) as usize;
251+
let mid = (offset + fib_val.min(arr.len() - offset - 1)).min(arr.len() - 1);
252+
probes.push(mid);
253+
254+
let cmp_result = cmp(&arr[mid], target);
255+
match cmp_result {
256+
0 => {
257+
TOTAL_COMPARISONS.fetch_add(comparisons, Ordering::Relaxed);
258+
return (Ok(mid), probes);
259+
}
260+
n if n < 0 => {
261+
offset = mid + 1;
262+
fib_idx = fib_idx.saturating_sub(2);
263+
}
264+
_ => {
265+
fib_idx = fib_idx.saturating_sub(1);
266+
}
267+
}
268+
269+
if offset >= arr.len() {
270+
break;
271+
}
272+
}
273+
274+
TOTAL_COMPARISONS.fetch_add(comparisons, Ordering::Relaxed);
275+
(Err(offset), probes)
276+
}
277+
228278
/// Standard binary search (for comparison/benchmarking).
229279
///
230280
/// This is provided as a reference implementation to compare against

0 commit comments

Comments
 (0)