@@ -1538,4 +1538,187 @@ mod tests {
15381538
15391539 assert ! ( accuracy_focus > 1.0 / n_classes as f64 , "should beat random" ) ;
15401540 }
1541+ #[ test]
1542+ #[ ignore]
1543+ fn test_multi_scan_nars_revision ( ) {
1544+ // Multiple scan strategies with NARS evidence revision.
1545+ // Each scan is independent evidence. Revision increases confidence.
1546+ // Stop when confidence > threshold (elevation cascade).
1547+
1548+ let bytes = match std:: fs:: read ( "/tmp/tiny_imagenet_labeled.bin" ) {
1549+ Ok ( b) => b,
1550+ Err ( _) => { eprintln ! ( "SKIP: /tmp/tiny_imagenet_labeled.bin not found" ) ; return ; }
1551+ } ;
1552+
1553+ let n = u32:: from_le_bytes ( [ bytes[ 0 ] , bytes[ 1 ] , bytes[ 2 ] , bytes[ 3 ] ] ) as usize ;
1554+ let d = u32:: from_le_bytes ( [ bytes[ 4 ] , bytes[ 5 ] , bytes[ 6 ] , bytes[ 7 ] ] ) as usize ;
1555+ let n_classes = u32:: from_le_bytes ( [ bytes[ 8 ] , bytes[ 9 ] , bytes[ 10 ] , bytes[ 11 ] ] ) as usize ;
1556+ let mut labels = Vec :: with_capacity ( n) ;
1557+ for i in 0 ..n {
1558+ let off = 12 + i * 4 ;
1559+ labels. push ( u32:: from_le_bytes ( [ bytes[ off] , bytes[ off+1 ] , bytes[ off+2 ] , bytes[ off+3 ] ] ) as usize ) ;
1560+ }
1561+ let pixel_start = 12 + n * 4 ;
1562+ let img_w = 64usize ; let img_h = 64usize ; let ch = 3usize ;
1563+ let n_use = n. min ( 200 ) ;
1564+
1565+ let pixel = |img : usize , r : usize , c : usize , channel : usize | -> f64 {
1566+ let off = pixel_start + ( img * d + r * img_w * ch + c * ch + channel) * 4 ;
1567+ f32:: from_le_bytes ( [ bytes[ off] , bytes[ off+1 ] , bytes[ off+2 ] , bytes[ off+3 ] ] ) as f64
1568+ } ;
1569+ let luma = |img : usize , r : usize , c : usize | -> f64 {
1570+ 0.299 * pixel ( img, r, c, 0 ) + 0.587 * pixel ( img, r, c, 1 ) + 0.114 * pixel ( img, r, c, 2 )
1571+ } ;
1572+
1573+ // ── NARS revision ──
1574+ fn nars_revision ( f1 : f64 , c1 : f64 , f2 : f64 , c2 : f64 ) -> ( f64 , f64 ) {
1575+ let w1 = c1 / ( 1.0 - c1 + 1e-9 ) ;
1576+ let w2 = c2 / ( 1.0 - c2 + 1e-9 ) ;
1577+ let w = w1 + w2;
1578+ let f = ( w1 * f1 + w2 * f2) / ( w + 1e-9 ) ;
1579+ let c = w / ( w + 1.0 ) ;
1580+ ( f. clamp ( 0.0 , 1.0 ) , c. clamp ( 0.0 , 0.999 ) )
1581+ }
1582+
1583+ // ── Scan strategy: extract features from a region ──
1584+ fn extract_patch ( pixel_fn : & dyn Fn ( usize , usize , usize ) -> f64 ,
1585+ r_center : usize , c_center : usize , radius : usize ,
1586+ img_h : usize , img_w : usize , ch : usize ) -> Vec < f64 > {
1587+ let mut f = Vec :: new ( ) ;
1588+ let r0 = r_center. saturating_sub ( radius) ;
1589+ let r1 = ( r_center + radius) . min ( img_h) ;
1590+ let c0 = c_center. saturating_sub ( radius) ;
1591+ let c1 = ( c_center + radius) . min ( img_w) ;
1592+ for r in r0..r1 { for c in c0..c1 { for channel in 0 ..ch {
1593+ f. push ( pixel_fn ( r, c, channel) ) ;
1594+ } } }
1595+ f
1596+ }
1597+
1598+ // ── Build archetypes for each scan strategy ──
1599+ // Strategy 1: NW intersection (1/3, 1/3), 8×8 patch
1600+ // Strategy 2: NE intersection (1/3, 2/3), 8×8 patch
1601+ // Strategy 3: SW intersection (2/3, 1/3), 8×8 patch
1602+ // Strategy 4: SE intersection (2/3, 2/3), 8×8 patch
1603+ // Strategy 5: center crop, 12×12 patch
1604+ // Strategy 6: horizontal midline
1605+ // Strategy 7: vertical midline
1606+
1607+ struct ScanStrategy {
1608+ name : & ' static str ,
1609+ extract : Box < dyn Fn ( usize ) -> Vec < f64 > > ,
1610+ }
1611+
1612+ // Build per-class archetypes for each strategy, then score
1613+ let intersections = [
1614+ ( "NW patch" , img_h/3 , img_w/3 , 4usize ) ,
1615+ ( "NE patch" , img_h/3 , 2 * img_w/3 , 4 ) ,
1616+ ( "SW patch" , 2 * img_h/3 , img_w/3 , 4 ) ,
1617+ ( "SE patch" , 2 * img_h/3 , 2 * img_w/3 , 4 ) ,
1618+ ( "Center" , img_h/2 , img_w/2 , 6 ) ,
1619+ ] ;
1620+
1621+ // For each strategy, build archetypes and classify
1622+ let mut strategy_scores: Vec < Vec < ( usize , f64 ) > > = vec ! [ Vec :: new( ) ; n_use] ; // per-image: [(class, similarity)]
1623+
1624+ for & ( name, cr, cc, radius) in & intersections {
1625+ // Extract features for all images
1626+ let features: Vec < Vec < f64 > > = ( 0 ..n_use) . map ( |img| {
1627+ let p = |r : usize , c : usize , channel : usize | pixel ( img, r, c, channel) ;
1628+ extract_patch ( & p, cr, cc, radius, img_h, img_w, ch)
1629+ } ) . collect ( ) ;
1630+
1631+ if features[ 0 ] . is_empty ( ) { continue ; }
1632+ let fd = features[ 0 ] . len ( ) ;
1633+
1634+ // Build archetypes
1635+ let mut arch = vec ! [ vec![ 0.0 ; fd] ; n_classes] ;
1636+ let mut cnt = vec ! [ 0usize ; n_classes] ;
1637+ for ( i, & l) in labels[ ..n_use] . iter ( ) . enumerate ( ) {
1638+ for j in 0 ..fd { arch[ l] [ j] += features[ i] [ j] ; }
1639+ cnt[ l] += 1 ;
1640+ }
1641+ for c in 0 ..n_classes {
1642+ if cnt[ c] > 0 { for j in 0 ..fd { arch[ c] [ j] /= cnt[ c] as f64 ; } }
1643+ }
1644+
1645+ // Score each image
1646+ for i in 0 ..n_use {
1647+ let mut best_c = 0 ;
1648+ let mut best_sim = f64:: NEG_INFINITY ;
1649+ for c in 0 ..n_classes {
1650+ if cnt[ c] == 0 { continue ; }
1651+ let dist: f64 = features[ i] . iter ( ) . zip ( & arch[ c] )
1652+ . map ( |( a, b) | ( a-b) * ( a-b) ) . sum :: < f64 > ( ) . sqrt ( ) ;
1653+ let sim = 1.0 / ( 1.0 + dist) ; // convert distance to similarity
1654+ if sim > best_sim { best_sim = sim; best_c = c; }
1655+ }
1656+ strategy_scores[ i] . push ( ( best_c, best_sim) ) ;
1657+ }
1658+ }
1659+
1660+ // ── Multi-scan NARS revision: combine all strategy votes ──
1661+ let mut correct_single = vec ! [ 0usize ; intersections. len( ) ] ; // per-strategy accuracy
1662+ let mut correct_revised = 0usize ;
1663+
1664+ for i in 0 ..n_use {
1665+ let true_label = labels[ i] ;
1666+
1667+ // Single strategy accuracies
1668+ for ( s, & ( pred_class, _) ) in strategy_scores[ i] . iter ( ) . enumerate ( ) {
1669+ if pred_class == true_label { correct_single[ s] += 1 ; }
1670+ }
1671+
1672+ // NARS revision: accumulate weighted evidence across all strategies.
1673+ // Each scan contributes its similarity as evidence weight for the class it detected.
1674+ // Confidence grows with number of agreeing scans (NARS: more evidence = more confident).
1675+ let mut class_evidence: Vec < f64 > = vec ! [ 0.0 ; n_classes] ; // total similarity weight
1676+ let mut class_votes: Vec < u32 > = vec ! [ 0 ; n_classes] ; // vote count
1677+
1678+ for & ( pred_class, similarity) in & strategy_scores[ i] {
1679+ class_evidence[ pred_class] += similarity;
1680+ class_votes[ pred_class] += 1 ;
1681+ }
1682+
1683+ // Pick class with highest accumulated evidence.
1684+ // NARS interpretation: frequency = avg similarity, confidence = vote proportion.
1685+ let total_scans = strategy_scores[ i] . len ( ) as f64 ;
1686+ let mut best_c = 0 ;
1687+ let mut best_score = f64:: NEG_INFINITY ;
1688+ for c in 0 ..n_classes {
1689+ if class_votes[ c] == 0 { continue ; }
1690+ let avg_sim = class_evidence[ c] / class_votes[ c] as f64 ;
1691+ let vote_frac = class_votes[ c] as f64 / total_scans;
1692+ // Combined: how similar (frequency) × how many agree (confidence)
1693+ let score = avg_sim * vote_frac;
1694+ if score > best_score { best_score = score; best_c = c; }
1695+ }
1696+ if best_c == true_label { correct_revised += 1 ; }
1697+ }
1698+
1699+ let revised_accuracy = correct_revised as f64 / n_use as f64 ;
1700+
1701+ eprintln ! ( "=== Multi-Scan NARS Revision ===" ) ;
1702+ eprintln ! ( " {} images, {} classes, {} scan strategies" , n_use, n_classes, intersections. len( ) ) ;
1703+ eprintln ! ( ) ;
1704+ eprintln ! ( " Per-strategy accuracy:" ) ;
1705+ for ( s, & ( name, _, _, _) ) in intersections. iter ( ) . enumerate ( ) {
1706+ let acc = correct_single[ s] as f64 / n_use as f64 ;
1707+ eprintln ! ( " {}: {:.1}% ({}/{})" , name, acc * 100.0 , correct_single[ s] , n_use) ;
1708+ }
1709+ eprintln ! ( ) ;
1710+ eprintln ! ( " NARS-revised (all strategies combined): {:.1}% ({}/{})" ,
1711+ revised_accuracy * 100.0 , correct_revised, n_use) ;
1712+ eprintln ! ( " Random baseline: {:.1}%" , 100.0 / n_classes as f64 ) ;
1713+ eprintln ! ( ) ;
1714+ let best_single = correct_single. iter ( ) . max ( ) . unwrap ( ) ;
1715+ let best_single_acc = * best_single as f64 / n_use as f64 ;
1716+ let improvement = revised_accuracy - best_single_acc;
1717+ eprintln ! ( " Improvement over best single scan: {:.1}%" , improvement * 100.0 ) ;
1718+ eprintln ! ( " This is NARS evidence accumulation — each scan adds confidence." ) ;
1719+
1720+ assert ! ( revised_accuracy > best_single_acc,
1721+ "NARS revision should improve over best single: {:.1}% vs {:.1}%" ,
1722+ revised_accuracy * 100.0 , best_single_acc * 100.0 ) ;
1723+ }
15411724}
0 commit comments