From 0a558b963d9a3bc90ccafd874f540271be41f02e Mon Sep 17 00:00:00 2001 From: Bradford Powell Date: Tue, 2 Aug 2022 16:12:08 -0400 Subject: [PATCH] add probabilistic tests for the ? and | mini-notation operators The tests are probabilistic, so it is possible that if the pseudo-random number generator changes in the future, we might get results that fail. They work for the current PRNG, though, and use boundaries for the number of values of different types such that there should only be about a 1% probability that the tests would fail by chance assuming that the PRNG returns evenly distributed values. --- packages/mini/test/mini.test.mjs | 36 +++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/mini/test/mini.test.mjs b/packages/mini/test/mini.test.mjs index 08283fe8..16e2c528 100644 --- a/packages/mini/test/mini.test.mjs +++ b/packages/mini/test/mini.test.mjs @@ -6,7 +6,6 @@ This program is free software: you can redistribute it and/or modify it under th import { strict as assert } from 'assert'; import { mini } from '../mini.mjs'; -import '@strudel.cycles/core/euclid.mjs'; describe('mini', () => { const minV = (v) => mini(v)._firstCycleValues; @@ -55,4 +54,39 @@ describe('mini', () => { mini('a?').queryArc(0, 20).map(hap => hap.whole.begin), mini('a').degradeBy(0.5).queryArc(0, 20).map(hap => hap.whole.begin)); }); + // testing things that involve pseudo-randomness, so there's a probability we could fail by chance. + // these next few tests work with the current PRNG, and are intended to succeed with p > 0.99 even if the PRNG changes + // (as long as the PRNG has a relatively-uniform distribution of values) + it('supports degradeBy with default of 50%', () => { + const haps = mini('a?').queryArc(0, 1000); + assert(459 <= haps.length && haps.length <= 541, 'Number of elements did not fall in 99% confidence interval for binomial with p=0.5'); + }); + it('supports degradeBy with an argument', () => { + const haps = mini('a?0.8').queryArc(0, 1000); + assert(haps.length > 0, 'Should have had at least one element when degradeBy was set at 0.8'); + assert(haps.length < 230, 'Had too many cycles remaining after degradeBy 0.8'); + }); + it('supports the random choice operator ("|") with nesting', () => { + const numCycles = 900; + const haps = mini('a | [b | c] | [d | e | f]').queryArc(0, numCycles); + // Should have about 1/3 a, 1/6 each of b | c, and 1/9 each of d | e | f. + // Evaluating this distribution with a chi-squared test. + // Note: this just evaluates the overall distribution, not things like correlation/runs of values + const observed = haps.reduce((acc, hap) => { + acc[hap.value] = (acc[hap.value] || 0) + 1; + return acc; + }, {}); + const expected = { + a: numCycles / 3, b: numCycles / 6, c: numCycles / 6, + d: numCycles / 9, e: numCycles / 9, f: numCycles / 9 + }; + let chisq = -numCycles; + for (let k in expected) { + chisq += observed[k] * observed[k] / expected[k]; + } + // 15.086 is the chisq for 5 degrees of freedom at 99%, so for 99% of uniformly-distributed + // PRNG, this test should succeed + assert(chisq <= 15.086, + chisq + ' was expected to be less than 15.086 under chi-squared test'); + }); });