Files
megahal.js/tests/sooth.test.ts
Chen Asraf 5413180b05 fix: sooth algorithm
test: add sooth tests
2024-08-03 02:33:19 +03:00

240 lines
7.3 KiB
TypeScript

import { SoothPredictor } from '../src/sooth' // Adjust the import according to your file structure
import fs from 'node:fs/promises'
describe('SoothPredictor', () => {
const errorEvent = 42
let predictor: SoothPredictor
beforeEach(() => {
predictor = new SoothPredictor(errorEvent)
})
describe('observe', () => {
it('increments counts', () => {
expect(predictor.observe(1, 3)).toBe(1)
expect(predictor.observe(1, 3)).toBe(2)
expect(predictor.observe(1, 3)).toBe(3)
})
it('sorts and finds contexts', () => {
expect(predictor.observe(2, 3)).toBe(1)
expect(predictor.observe(1, 2)).toBe(1)
expect(predictor.observe(3, 1)).toBe(1)
expect(predictor.observe(1, 2)).toBe(2)
expect(predictor.observe(2, 3)).toBe(2)
expect(predictor.observe(3, 1)).toBe(2)
})
})
describe('count', () => {
it('returns zero for an unobserved context', () => {
expect(predictor.count(1)).toBe(0)
})
it('returns the number of observations', () => {
predictor.observe(1, 2)
predictor.observe(1, 1)
predictor.observe(1, 4)
predictor.observe(1, 3)
predictor.observe(1, 0)
predictor.observe(1, 1)
predictor.observe(1, 4)
expect(predictor.count(1)).toBe(7)
})
})
describe('size', () => {
it('returns zero for an unobserved context', () => {
expect(predictor.size(1)).toBe(0)
})
it('returns the number of distinct events', () => {
predictor.observe(1, 2)
predictor.observe(1, 1)
predictor.observe(1, 4)
predictor.observe(1, 3)
predictor.observe(1, 0)
predictor.observe(1, 1)
predictor.observe(1, 4)
expect(predictor.size(1)).toBe(5)
})
})
describe('select', () => {
it('returns the error event for an unobserved context', () => {
expect(predictor.select(1, 1)).toBe(errorEvent)
})
it('returns the correct event for an observed context', () => {
predictor.observe(1, 4)
predictor.observe(1, 3)
predictor.observe(1, 4)
predictor.observe(1, 5)
expect(predictor.select(1, 1)).toBe(3)
expect(predictor.select(1, 2)).toBe(4)
expect(predictor.select(1, 3)).toBe(4)
expect(predictor.select(1, 4)).toBe(5)
})
it('returns the error event for a limit that is out of range', () => {
predictor.observe(1, 4)
predictor.observe(1, 3)
predictor.observe(1, 5)
expect(predictor.select(1, 0)).toBe(errorEvent)
expect(predictor.select(1, 4)).toBe(errorEvent)
})
it('selects the correct event with many contexts', () => {
predictor.observe(2, 4)
predictor.observe(1, 5)
predictor.observe(3, 6)
predictor.observe(1, 7)
predictor.observe(2, 8)
predictor.observe(3, 9)
predictor.observe(1, 1)
predictor.observe(2, 2)
predictor.observe(3, 3)
expect(predictor.select(2, 0)).toBe(errorEvent)
expect(predictor.select(2, 1)).toBe(2)
expect(predictor.select(2, 2)).toBe(4)
expect(predictor.select(2, 3)).toBe(8)
expect(predictor.select(2, 4)).toBe(errorEvent)
expect(predictor.select(1, 0)).toBe(errorEvent)
expect(predictor.select(1, 1)).toBe(1)
expect(predictor.select(1, 2)).toBe(5)
expect(predictor.select(1, 3)).toBe(7)
expect(predictor.select(1, 4)).toBe(errorEvent)
expect(predictor.select(3, 0)).toBe(errorEvent)
expect(predictor.select(3, 1)).toBe(3)
expect(predictor.select(3, 2)).toBe(6)
expect(predictor.select(3, 3)).toBe(9)
expect(predictor.select(3, 4)).toBe(errorEvent)
})
})
describe('clear', () => {
it('resets to a blank slate', () => {
expect(predictor.observe(1, 3)).toBe(1)
expect(predictor.observe(1, 3)).toBe(2)
expect(predictor.observe(1, 3)).toBe(3)
predictor.clear()
expect(predictor.observe(1, 3)).toBe(1)
expect(predictor.observe(1, 3)).toBe(2)
expect(predictor.observe(1, 3)).toBe(3)
})
})
describe('save and load', () => {
it('can save a file and load it back again', async () => {
const filePath = './sooth_spec.tmp'
try {
expect(predictor.observe(1, 3)).toBe(1)
expect(predictor.observe(2, 3)).toBe(1)
expect(predictor.observe(1, 3)).toBe(2)
expect(predictor.observe(1, 3)).toBe(3)
expect(() => predictor.save(filePath)).not.toThrow()
expect(predictor.count(1)).toBe(3)
expect(predictor.count(2)).toBe(1)
predictor.clear()
expect(predictor.count(1)).toBe(0)
expect(predictor.count(2)).toBe(0)
expect(() => predictor.load(filePath)).not.toThrow()
expect(predictor.count(1)).toBe(3)
expect(predictor.count(2)).toBe(1)
expect(predictor.observe(1, 3)).toBe(4)
expect(predictor.observe(1, 1)).toBe(1)
expect(predictor.observe(2, 3)).toBe(2)
expect(predictor.observe(2, 1)).toBe(1)
} finally {
await fs.unlink(filePath)
}
})
})
describe('distribution', () => {
it('has no distribution for a new context', () => {
expect(predictor.distribution(1)).toBeNull()
expect(predictor.count(1)).toBe(0)
expect(predictor.uncertainty(1)).toBeNull()
})
it('has a correct probability distribution', () => {
predictor.observe(1, 4)
predictor.observe(1, 2)
predictor.observe(1, 4)
predictor.observe(1, 3)
const dist = predictor.distribution(1)!
expect(dist[1]).toBeUndefined()
expect(dist[2]).toBe(0.25)
expect(dist[3]).toBe(0.25)
expect(dist[4]).toBe(0.5)
})
})
describe('uncertainty', () => {
it('has no uncertainty for a new context', () => {
expect(predictor.uncertainty(1)).toBeNull()
expect(predictor.count(1)).toBe(0)
expect(predictor.uncertainty(1)).toBeNull()
})
it('has zero uncertainty for a lone context', () => {
predictor.observe(1, 3)
expect(predictor.uncertainty(1)).toBe(0)
})
it('has maximal uncertainty for a uniform distribution', () => {
for (let i = 1; i <= 256; i++) {
predictor.observe(1, i)
}
expect(predictor.uncertainty(1)).toBe(8)
})
})
describe('surprise', () => {
it('has no surprise for a new context', () => {
expect(predictor.surprise(1, 3)).toBeNull()
})
it('has no surprise for a new event', () => {
predictor.observe(1, 3)
expect(predictor.surprise(1, 4)).toBeNull()
})
it('has zero surprise for a lone event', () => {
predictor.observe(1, 3)
expect(predictor.surprise(1, 3)).toBe(0)
})
it('has uniform surprise for a uniform distribution', () => {
for (let i = 1; i <= 256; i++) {
predictor.observe(1, i)
}
expect(predictor.surprise(1, 3)).toBe(8)
})
})
describe('frequency', () => {
it('returns zero for a new context', () => {
expect(predictor.frequency(1, 3)).toBe(0)
})
it('returns zero for a new event', () => {
predictor.observe(1, 3)
expect(predictor.frequency(1, 4)).toBe(0)
})
it('is one for a lone event', () => {
predictor.observe(1, 3)
expect(predictor.frequency(1, 3)).toBe(1)
})
it('is uniform for a uniform distribution', () => {
for (let i = 1; i <= 100; i++) {
predictor.observe(1, i)
}
expect(predictor.frequency(1, 3)).toBe(0.01)
})
})
})