Merge pull request #531 from ijc8/main

Maintain random seed state in parser, not globally
This commit is contained in:
Felix Roos 2023-03-23 10:18:58 +01:00 committed by GitHub
commit ddf61a6438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 43 additions and 45 deletions

View File

@ -279,7 +279,7 @@ function peg$parse(input, options) {
var peg$f8 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f9 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f12 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f13 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
for (const op of ops) {
@ -289,8 +289,8 @@ function peg$parse(input, options) {
};
var peg$f14 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f15 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f16 = function(tail) { return { alignment: 'rand', list: tail }; };
var peg$f17 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } };
var peg$f16 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f17 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f18 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f19 = function(sc) { return sc; };
var peg$f20 = function(s) { return { name: "struct", args: { mini:s }}};
@ -2185,10 +2185,13 @@ function peg$parse(input, options) {
this.location_ = location();
}
var PatternStub = function(source, alignment)
var PatternStub = function(source, alignment, seed)
{
this.type_ = "pattern";
this.arguments_ = { alignment : alignment};
this.arguments_ = { alignment: alignment };
if (seed !== undefined) {
this.arguments_.seed = seed;
}
this.source_ = source;
}
@ -2214,6 +2217,7 @@ function peg$parse(input, options) {
this.options_ = options;
}
var seed = 0;
peg$result = peg$startRuleFunction();

View File

@ -19,10 +19,13 @@ This program is free software: you can redistribute it and/or modify it under th
this.location_ = location();
}
var PatternStub = function(source, alignment)
var PatternStub = function(source, alignment, seed)
{
this.type_ = "pattern";
this.arguments_ = { alignment : alignment};
this.arguments_ = { alignment: alignment };
if (seed !== undefined) {
this.arguments_.seed = seed;
}
this.source_ = source;
}
@ -48,6 +51,7 @@ This program is free software: you can redistribute it and/or modify it under th
this.options_ = options;
}
var seed = 0;
}
start = statement
@ -137,7 +141,7 @@ op_fast = "*"a:slice
{ return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) }
op_degrade = "?"a:number?
{ return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) }
{ return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) }
op_tail = ":" s:slice
{ return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) }
@ -162,12 +166,12 @@ stack_tail = tail:(comma @sequence)+
// a choose is a series of pipe-separated sequence, one of which is
// chosen at random, each cycle
choose_tail = tail:(pipe @sequence)+
{ return { alignment: 'rand', list: tail }; }
{ return { alignment: 'rand', list: tail, seed: seed++ }; }
// if the stack contains only one element, we don't create a stack but return the
// underlying element
stack_or_choose = head:sequence tail:(stack_tail / choose_tail)?
{ if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } }
{ if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } }
polymeter_stack = head:sequence tail:stack_tail?
{ return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); }

View File

@ -7,12 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th
import * as krill from './krill-parser.js';
import * as strudel from '@strudel.cycles/core';
/* var _seedState = 0;
const randOffset = 0.0002;
function _nextSeed() {
return _seedState++;
} */
const randOffset = 0.0003;
const applyOptions = (parent, code) => (pat, i) => {
const ast = parent.source_[i];
@ -44,27 +39,9 @@ const applyOptions = (parent, code) => (pat, i) => {
break;
}
case 'degradeBy': {
// TODO: find out what is right here
// example:
/*
stack(
s("hh*8").degrade(),
s("[ht*8]?")
)
*/
// above example will only be in sync when _degradeBy is used...
// it also seems that the nextSeed will create undeterministic behaviour
// as it uses a global _seedState. This is probably the reason for
// https://github.com/tidalcycles/strudel/issues/245
// this is how it was:
/*
return strudel.reify(pat)._degradeByWith(
strudel.rand.early(randOffset * _nextSeed()).segment(1),
op.arguments_.amount ?? 0.5,
);
*/
pat = strudel.reify(pat).degradeBy(op.arguments_.amount === null ? 0.5 : op.arguments_.amount);
pat = strudel
.reify(pat)
._degradeByWith(strudel.rand.early(randOffset * op.arguments_.seed), op.arguments_.amount ?? 0.5);
break;
}
case 'tail': {
@ -114,9 +91,7 @@ export function patternifyAST(ast, code) {
return strudel.stack(...aligned);
}
if (alignment === 'rand') {
// https://github.com/tidalcycles/strudel/issues/245#issuecomment-1345406422
// return strudel.chooseInWith(strudel.rand.early(randOffset * _nextSeed()).segment(1), children);
return strudel.chooseCycles(...children);
return strudel.chooseInWith(strudel.rand.early(randOffset * ast.arguments_.seed).segment(1), children);
}
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
if (!weightedChildren && alignment === 'slowcat') {

View File

@ -140,10 +140,22 @@ describe('mini', () => {
expect(haps.length < 230).toBe(true);
// 'Had too many cycles remaining after degradeBy 0.8');
});
it('supports lists', () => {
expect(minV('a:b c:d:[e:f] g')).toEqual([['a', 'b'], ['c', 'd', ['e', 'f']], 'g']);
it('supports multiple independent uses of the random choice operator ("|")', () => {
const numCycles = 1000;
const values = mini('[a|b] [a|b]')
.queryArc(0, numCycles)
.map((e) => e.value);
const observed = { aa: 0, ab: 0, ba: 0, bb: 0 };
for (let i = 0; i < values.length; i += 2) {
const chunk = values.slice(i, i + 2);
observed[chunk.join('')]++;
}
for (const count of Object.values(observed)) {
// Should fall within 99% confidence interval for binomial with p=0.25.
expect(215 <= count && count <= 286).toBe(true);
}
});
/*it('supports the random choice operator ("|") with nesting', () => {
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.
@ -168,6 +180,8 @@ describe('mini', () => {
// 15.086 is the chisq for 5 degrees of freedom at 99%, so for 99% of uniformly-distributed
// PRNG, this test should succeed
expect(chisq <= 15.086).toBe(true);
// assert(chisq <= 15.086, chisq + ' was expected to be less than 15.086 under chi-squared test');
});*/
});
it('supports lists', () => {
expect(minV('a:b c:d:[e:f] g')).toEqual([['a', 'b'], ['c', 'd', ['e', 'f']], 'g']);
});
});

View File

@ -63,6 +63,7 @@ exports[`renders tunes > tune: arpoon 1`] = `
"[ 0/1 → 1/2 | s:bd bank:RolandTR909 gain:0.5 ]",
"[ 1/2 → 1/1 | s:bd bank:RolandTR909 gain:0.5 ]",
"[ 1/2 → 2/3 | s:hh bank:RolandTR909 gain:0.5 ]",
"[ 5/6 → 1/1 | s:hh bank:RolandTR909 gain:0.5 ]",
]
`;