mirror of
https://github.com/chenasraf/megahal.js.git
synced 2026-05-17 17:48:02 +00:00
240 lines
7.3 KiB
TypeScript
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)
|
|
})
|
|
})
|
|
})
|