gaussian_test.go raw

   1  package crypto
   2  
   3  import (
   4  	"testing"
   5  
   6  	"git.mleku.dev/mleku/dendrite/pkg/axiom"
   7  	"git.mleku.dev/mleku/dendrite/pkg/lattice"
   8  	"git.mleku.dev/mleku/dendrite/pkg/ratio"
   9  )
  10  
  11  type testElem struct {
  12  	tag string
  13  	val string
  14  }
  15  
  16  func (e testElem) Type() string { return e.tag }
  17  func (e testElem) Value() any   { return e.val }
  18  
  19  func buildBondedLattice(n int) *lattice.Lattice {
  20  	l := lattice.New()
  21  	nodes := make([]*lattice.Node, n)
  22  	for i := range nodes {
  23  		nodes[i] = l.AddNode([]axiom.Constraint{tagConstraint{"word"}})
  24  	}
  25  	// Ring topology.
  26  	for i := range nodes {
  27  		l.Connect(nodes[i], nodes[(i+1)%len(nodes)])
  28  	}
  29  	// Bond half the nodes.
  30  	for i := 0; i < n/2; i++ {
  31  		nodes[i].Bond(testElem{"word", string(rune('a' + i))})
  32  	}
  33  	return l
  34  }
  35  
  36  func TestNewSampler(t *testing.T) {
  37  	l := buildBondedLattice(20)
  38  	gs := NewSampler(l, ratio.New(4, 10))
  39  
  40  	if gs.Lattice != l {
  41  		t.Error("sampler should reference the lattice")
  42  	}
  43  	if !gs.Threshold.Equal(ratio.New(4, 10)) {
  44  		t.Errorf("threshold = %s, want 4/10", gs.Threshold)
  45  	}
  46  }
  47  
  48  func TestSampleReturnsPattern(t *testing.T) {
  49  	l := buildBondedLattice(20)
  50  	gs := NewSampler(l, ratio.New(4, 10))
  51  
  52  	marks := gs.Sample()
  53  	if len(marks) != l.Size() {
  54  		t.Errorf("sample length = %d, want %d", len(marks), l.Size())
  55  	}
  56  
  57  	// At least some should be occupied (high lock-in survivors).
  58  	occupied := 0
  59  	for _, m := range marks {
  60  		if m.Occupied {
  61  			occupied++
  62  		}
  63  	}
  64  	// We bonded 10 of 20 nodes. With ring topology each bonded node
  65  	// has ~1 bonded neighbor out of 2, giving contextual lock-in of
  66  	// 0.3 + 0.7*(1/2) = 0.65 > 0.4 threshold. Most should survive.
  67  	if occupied == 0 {
  68  		t.Error("expected at least some occupied sites after sampling")
  69  	}
  70  }
  71  
  72  func TestNoiseVectorLength(t *testing.T) {
  73  	l := buildBondedLattice(16)
  74  	gs := NewSampler(l, ratio.New(8, 10)) // high threshold = more dissolution
  75  
  76  	vec, _ := gs.NoiseVector()
  77  	if len(vec) != l.Size() {
  78  		t.Errorf("noise vector length = %d, want %d", len(vec), l.Size())
  79  	}
  80  }
  81  
  82  func TestSnapshotConsistency(t *testing.T) {
  83  	l := buildBondedLattice(10)
  84  	s1 := snapshot(l)
  85  	s2 := snapshot(l)
  86  
  87  	if len(s1) != len(s2) {
  88  		t.Fatal("snapshots should have same length")
  89  	}
  90  	for i := range s1 {
  91  		if s1[i].Occupied != s2[i].Occupied {
  92  			t.Errorf("site %d occupancy mismatch", i)
  93  		}
  94  		if s1[i].TypeTag != s2[i].TypeTag {
  95  			t.Errorf("site %d type tag mismatch", i)
  96  		}
  97  	}
  98  }
  99