Skip to content

Commit

Permalink
Implement Wilson height calculation
Browse files Browse the repository at this point in the history
Update xen-dev-utils dependency.
Add 'wilson' as a coalescing option.
Fix autodoc.
  • Loading branch information
frostburn committed Apr 30, 2024
1 parent 051ac69 commit 8a44493
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 18 deletions.
7 changes: 5 additions & 2 deletions documentation/BUILTIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ Prepend an interval at the beginning of the current/given scale.
### warn(*...args*)
Print the arguments to the console with "warning" emphasis.

### wilsonHeight(*interval*)
Calculate the Wilson height of the interval. Sum of prime absolute factors with repetition..

### withEquave(*val*, *equave*)
Change the equave of the val.

Expand Down Expand Up @@ -420,10 +423,10 @@ Calculate the geometric difference of two intervals on a circle.
Calculate the geometric distance of two intervals on a circle.

### coalesce(*tolerance = 3.5*, *action = "simplest"*, *preserveBoundary = false*, *scale = $$*)
Coalesce intervals in the current/given scale separated by `tolerance` (default 3.5 cents) into one. `action` is one of 'simplest', 'lowest', 'highest', 'avg', 'havg' or 'geoavg' defaulting to 'simplest'. If `preserveBoundary` is `true` intervals close to unison and the equave are not eliminated.
Coalesce intervals in the current/given scale separated by `tolerance` (default 3.5 cents) into one. `action` is one of 'simplest', 'wilson', 'lowest', 'highest', 'avg', 'havg' or 'geoavg' defaulting to 'simplest'. If `preserveBoundary` is `true` intervals close to unison and the equave are not eliminated.

### coalesced(*tolerance = 3.5*, *action = "simplest"*, *preserveBoundary = false*, *scale = $$*)
Obtain a copy of the current/given scale where groups of intervals separated by `tolerance` are coalesced into one. `action` is one of 'simplest', 'lowest', 'highest', 'avg', 'havg' or 'geoavg'. If `preserveBoundary` is `true` intervals close to unison and the equave are not eliminated.
Obtain a copy of the current/given scale where groups of intervals separated by `tolerance` are coalesced into one. `action` is one of 'simplest', 'wilson', 'lowest', 'highest', 'avg', 'havg' or 'geoavg'. If `preserveBoundary` is `true` intervals close to unison and the equave are not eliminated.

### colorsOf(*scale = $$*)
Obtain an array of colors of the current/given scale.
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
},
"dependencies": {
"moment-of-symmetry": "^0.4.2",
"xen-dev-utils": "^0.4.0"
"xen-dev-utils": "^0.5.0"
},
"engines": {
"node": ">=12.0.0"
Expand Down
4 changes: 2 additions & 2 deletions scripts/builtin-docs.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {stdout} from 'process';
import {getSourceVisitor} from '../src/parser';
import {getGlobalVisitor} from '../src/parser';
import {SonicWeaveFunction} from '../src/stdlib';
import {expressionToString} from '../src/ast';

const visitor = getSourceVisitor();
const visitor = getGlobalVisitor();

stdout.write('# SonicWeave standard library\n');
stdout.write(
Expand Down
10 changes: 10 additions & 0 deletions src/parser/__tests__/expression.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2262,4 +2262,14 @@ describe('Poor grammar / Fun with "<"', () => {
expect(oof[0].valueOf()).toBe(1);
expect(oof[1].a.valueOf()).toBe(2);
});

it('can measure the Wilson complexity of 11/8', () => {
const {fraction} = parseSingle('wilsonHeight(11/8)');
expect(fraction).toBe('17');
});

it('can measure the Wilson complexity of 1073741820', () => {
const {fraction} = parseSingle('wilsonHeight(1073741820)');
expect(fraction).toBe((2 + 2 + 3 + 5 + 29 + 43 + 113 + 127).toString());
});
});
5 changes: 5 additions & 0 deletions src/parser/__tests__/stdlib.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,11 @@ describe('SonicWeave standard library', () => {
expect(scale).toEqual(['4096/4095', '3/2', '4095/2048', '2/1']);
});

it('coalesces based on wilson height', () => {
const scale = expand('70/69;37/36;P5;2;coalesce(23., "wilson")');
expect(scale).toEqual(['70/69', 'P5', '2']);
});

it('calculates Euler-Fokker genus 444', () => {
const scale = expand('eulerGenus(444)');
expect(scale).toEqual(['37/32', '3/2', '111/64', '2']);
Expand Down
1 change: 1 addition & 0 deletions src/parser/__tests__/vector-broadcasting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ describe('SonicWeave vector broadcasting', () => {
'JIP',
'PrimeMapping(1200., 1902.)',
'tenneyHeight',
'wilsonHeight',
'floor',
'round',
'trunc',
Expand Down
17 changes: 17 additions & 0 deletions src/stdlib/builtin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
absolute as pubAbsolute,
relative as pubRelative,
tenneyHeight as pubTenney,
wilsonHeight as pubWilson,
track as pubTrack,
sort as pubSort,
repr as pubRepr,
Expand Down Expand Up @@ -1372,6 +1373,21 @@ tenneyHeight.__doc__ =
'Calculate the Tenney height of the interval. Natural logarithm of numerator times denominator.';
tenneyHeight.__node__ = builtinNode(tenneyHeight);

// TODO: Wilson in cosJIP, nthPrime, primeRange, primeMonzo
function wilsonHeight(
this: ExpressionVisitor,
interval: SonicWeaveValue
): SonicWeaveValue {
requireParameters({interval});
if (typeof interval === 'boolean' || interval instanceof Interval) {
return pubWilson.bind(this)(interval);
}
return unaryBroadcast.bind(this)(interval, wilsonHeight.bind(this));
}
wilsonHeight.__doc__ =
'Calculate the Wilson height of the interval. Sum of prime absolute factors with repetition..';
wilsonHeight.__node__ = builtinNode(wilsonHeight);

function gcd(
this: ExpressionVisitor,
x: SonicWeaveValue,
Expand Down Expand Up @@ -2277,6 +2293,7 @@ export const BUILTIN_CONTEXT: Record<string, Interval | SonicWeaveFunction> = {
JIP,
PrimeMapping,
tenneyHeight,
wilsonHeight,
gcd,
lcm,
hasConstantStructure: hasConstantStructure_,
Expand Down
7 changes: 5 additions & 2 deletions src/stdlib/prelude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,7 @@ riff randomVaried(amount, varyEquave = false, scale = $$) {
riff coalesced(tolerance = 3.5, action = 'simplest', preserveBoundary = false, scale = $$) {
"Obtain a copy of the current/given scale where groups of intervals separated by \`tolerance\` are coalesced into one.\\
\`action\` is one of 'simplest', 'lowest', 'highest', 'avg', 'havg' or 'geoavg'.\\
\`action\` is one of 'simplest', 'wilson', 'lowest', 'highest', 'avg', 'havg' or 'geoavg'.\\
If \`preserveBoundary\` is \`true\` intervals close to unison and the equave are not eliminated.";
if (not scale) return [];
Expand All @@ -957,6 +957,9 @@ riff coalesced(tolerance = 3.5, action = 'simplest', preserveBoundary = false, s
havg(...group);
} else if (action === 'geoavg') {
geoavg(...group);
} else if (action === 'wilson') {
sort(group, (a, b) => wilsonHeight(a) - wilsonHeight(b));
group[0];
} else {
sort(group, (a, b) => tenneyHeight(a) - tenneyHeight(b));
group[0];
Expand All @@ -980,7 +983,7 @@ riff coalesced(tolerance = 3.5, action = 'simplest', preserveBoundary = false, s
riff coalesce(tolerance = 3.5, action = 'simplest', preserveBoundary = false, scale = $$) {
"Coalesce intervals in the current/given scale separated by \`tolerance\` (default 3.5 cents) into one. \\
\`action\` is one of 'simplest', 'lowest', 'highest', 'avg', 'havg' or 'geoavg' defaulting to 'simplest'.\\
\`action\` is one of 'simplest', 'wilson', 'lowest', 'highest', 'avg', 'havg' or 'geoavg' defaulting to 'simplest'.\\
If \`preserveBoundary\` is \`true\` intervals close to unison and the equave are not eliminated.";
$ = scale;
scale = $[..];
Expand Down
42 changes: 35 additions & 7 deletions src/stdlib/public.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/**
* Exported builtins without vectorization complications.
*/
import {Fraction, LOG_PRIMES} from 'xen-dev-utils';
import {
Fraction,
tenneyHeight as xduTenney,
wilsonHeight as xduWilson,
} from 'xen-dev-utils';
import {Color, Interval, Val} from '../interval';
import {type ExpressionVisitor} from '../parser/expression';
import {NEGATIVE_ONE, TWO} from '../utils';
import {FRACTION_PRIMES, NEGATIVE_ONE, TWO} from '../utils';
import {SonicWeavePrimitive, SonicWeaveValue, upcastBool} from './runtime';
import {TimeMonzo, TimeReal} from '../monzo';

Expand Down Expand Up @@ -213,14 +217,38 @@ export function tenneyHeight(
return new Interval(TimeReal.fromValue(Infinity), 'linear');
}
const height =
Math.log(monzo.residual.n * monzo.residual.d) +
monzo.primeExponents.reduce(
(total, pe, i) => total + Math.abs(pe.valueOf()) * LOG_PRIMES[i],
0
);
xduTenney(monzo.residual) +
xduTenney(monzo.primeExponents.map(pe => pe.valueOf()));
return new Interval(TimeReal.fromValue(height), 'linear');
}

/**
* Calculate the Wilson height of the interval. Sum of prime absolute factors with repetition.
* @param this {@link ExpressionVisitor} instance providing context for the height of absolute intervals.
* @param interval Interval to measure.
* @returns Relative linear interval representing the Wilson height.
*/
export function wilsonHeight(
this: ExpressionVisitor,
interval: Interval | boolean
): Interval {
const monzo = relative.bind(this)(upcastBool(interval)).value;
if (monzo instanceof TimeReal) {
return new Interval(TimeReal.fromValue(Infinity), 'linear');
}
const resHeight = xduWilson(monzo.residual);
if (resHeight === Infinity) {
return new Interval(TimeReal.fromValue(Infinity), 'linear');
}
let result = new Fraction(resHeight);
const pe = monzo.primeExponents;
for (let i = 0; i < pe.length; ++i) {
result = result.add(pe[i].abs().mul(FRACTION_PRIMES[i]));
}

return new Interval(TimeMonzo.fromFraction(result), 'linear');
}

/**
* Attach a tracking ID to the interval.
* @param this {@link ExpressionVisitor} instance providing context for the next tracking ID.
Expand Down

0 comments on commit 8a44493

Please sign in to comment.