SymbolicPower Algebra
Understanding how RMT Compose handles irrational numbers like 2^(1/12) with full algebraic precision.
The Problem with Irrational Numbers
Equal temperament tuning produces irrational numbers. For example:
12-TET semitone = 2^(1/12) = 1.0594630943592953...This number cannot be exactly represented as a fraction or floating-point number. If we just used floats:
// Float multiplication loses precision
const semitone = Math.pow(2, 1/12) // 1.0594630943592953
const octave = semitone ** 12 // 1.9999999999999998 (not exactly 2!)SymbolicPower: Algebraic Precision
RMT Compose uses SymbolicPower to preserve the algebraic structure of expressions.
How It Works
Instead of computing 2^(1/12) as a float, SymbolicPower stores:
- Base: 2
- Exponent: 1/12
This representation is exact and allows perfect algebraic simplification.
Automatic Simplification
// 2^(1/12) × 2^(1/12) = 2^(2/12) = 2^(1/6)
// Exponents add: 1/12 + 1/12 = 2/12 = 1/6
// Full octave: (2^(1/12))^12 = 2^1 = 2 (exactly!)RMT Compose handles this automatically when you chain TET operations.
Creating SymbolicPower Values
Basic Syntax
// 2 to the power of 1/12
new Fraction(2).pow(new Fraction(1, 12))
// 3 to the power of 1/13 (Bohlen-Pierce)
new Fraction(3).pow(new Fraction(1, 13))Chained Operations
// Major third in 12-TET (4 semitones)
module.baseNote.getVariable('frequency')
.mul(new Fraction(2).pow(new Fraction(4, 12)))
// Same result, built step by step
module.baseNote.getVariable('frequency')
.mul(new Fraction(2).pow(new Fraction(1, 12)))
.mul(new Fraction(2).pow(new Fraction(1, 12)))
.mul(new Fraction(2).pow(new Fraction(1, 12)))
.mul(new Fraction(2).pow(new Fraction(1, 12)))Both produce exactly 2^(4/12) = 2^(1/3).
Algebraic Rules Applied
Multiplication
When multiplying SymbolicPower values with the same base:
a^m × a^n = a^(m+n)// 2^(1/12) × 2^(3/12) = 2^(4/12)Division
a^m ÷ a^n = a^(m-n)// Going down a semitone
frequency.div(new Fraction(2).pow(new Fraction(1, 12)))
// = frequency × 2^(-1/12)Power of Power
(a^m)^n = a^(m×n)// Octave check: (2^(1/12))^12 = 2^1 = 2Mixed Operations with Fractions
SymbolicPower values can multiply with regular fractions:
// 440 × 2^(7/12) = 659.25... Hz (perfect fifth in 12-TET)
new Fraction(440).mul(new Fraction(2).pow(new Fraction(7, 12)))The system tracks both components:
- Rational part: 440
- Irrational part: 2^(7/12)
The ≈ Display
When a value contains SymbolicPower components, the Variable Widget shows:
≈ 659.26 HzThe ≈ indicates:
- The displayed number is an approximation
- The actual stored value is algebraically exact
- Further calculations use the exact symbolic form
Why This Matters
Scenario: Building a Full Chromatic Scale
Without SymbolicPower:
// Float accumulation error
let freq = 440
for (let i = 0; i < 12; i++) {
freq *= 1.0594630943592953
}
// freq = 879.9999999999999 (not exactly 880!)With SymbolicPower:
// Algebraic precision
// 440 × (2^(1/12))^12 = 440 × 2 = 880 exactlyScenario: Transposition
When transposing a melody:
// Transpose up a major third (4 semitones)
originalFreq.mul(new Fraction(2).pow(new Fraction(4, 12)))The system preserves relationships:
If note1 = 440 × 2^(3/12)
And note2 = note1 × 2^(4/12)
Then note2 = 440 × 2^(7/12) // Exact!Advanced: Different Bases
Bohlen-Pierce (Base 3)
// Tritave (3:1) divided into 13 parts
new Fraction(3).pow(new Fraction(1, 13))SymbolicPower handles any integer base:
- Base 2: Standard octave-based temperaments
- Base 3: Tritave-based scales like Bohlen-Pierce
- Other bases: Experimental tuning systems
Combining Different Bases
When multiplying different bases, the system tracks them separately:
// 2^(1/12) × 3^(1/13) - tracked as compound symbolic valueInternal Representation
For developers, here's how SymbolicPower works internally:
Structure
{
type: 'symbolic_power',
base: Fraction(2), // The base number
exponent: Fraction(1, 12), // The exponent
coefficient: Fraction(440) // Optional rational multiplier
}Evaluation Pipeline
- Parse: Expression text → AST
- Compile: AST → Bytecode (SymbolicPower opcodes)
- Evaluate: Stack VM evaluates, preserving symbolic structure
- Display: Evaluate to float only for final display
Bytecode Operations
The binary evaluator has specific opcodes for symbolic operations:
POW: Creates SymbolicPower from base and exponentMUL_SYM: Multiplies preserving symbolic structureDIV_SYM: Divides preserving symbolic structure
Practical Examples
12-TET Scale with Perfect Octave
// Root
frequency: module.baseNote.getVariable('frequency')
// Each subsequent note adds one semitone
// Note N: frequency × 2^(N/12)
// After 12 notes, we get:
frequency: module.baseNote.getVariable('frequency')
.mul(new Fraction(2).pow(new Fraction(12, 12)))
// = baseFreq × 2 (exactly one octave, no drift!)Stacking Fifths
// Perfect fifth in 12-TET = 7 semitones
// Stack 12 fifths: (2^(7/12))^12 = 2^7 = 128 (7 octaves exactly)
// But musically, 12 pure fifths = (3/2)^12 = 129.746...
// The Pythagorean comma! 12-TET eliminates this by distributing the error.Verification
Test that 12 semitones = 1 octave:
// Build note 12 semitones up
const twelveUp = module.baseNote.getVariable('frequency')
.mul(new Fraction(2).pow(new Fraction(12, 12)))
// This simplifies to:
// baseFreq × 2^1 = baseFreq × 2
// The evaluated value will be exactly 2× the base frequencyLimitations
What SymbolicPower Handles
- Powers of integers:
2^(1/12),3^(1/13) - Products of symbolic powers
- Mixed rational and irrational values
What Requires Approximation
- Addition of irrational values:
2^(1/12) + 2^(1/19)(no closed form) - Transcendental operations on symbolic values
- Nested irrational exponents
For these cases, the system falls back to high-precision float approximation.
Debugging Tips
Check Symbolic Preservation
- Create a note with TET expression
- Look for ≈ in the display
- Chain 12 semitones and verify exact octave
Verify Algebraic Simplification
// This should display exactly 2× the base frequency
frequency: module.baseNote.getVariable('frequency')
.mul(new Fraction(2).pow(new Fraction(1, 12)))
.mul(new Fraction(2).pow(new Fraction(1, 12)))
// ... (repeat 12 times)Next Steps
- Complex Dependencies - Build sophisticated note relationships
- Microtonal Composition - Apply symbolic algebra to microtonal music
- Expression Compiler - Technical deep dive