topos-of-music
Topos of Music Skill
Trit: +1 (PLUS - generator) Color: Red (#D82626)
Overview
Implements Guerino Mazzola's Topos of Music categorical framework:
- Forms: Types in the musical topos (Simple, Limit, Colimit, List)
- Denotators: Instances of forms (notes, chords, scores)
- Morphisms: Structure-preserving transformations
- Neo-Riemannian: PLR group operations on triads
Forms (Types)
abstract type Form end
struct SimpleForm <: Form
name::Symbol
module_type::Symbol # :Z, :R, :Q
end
struct LimitForm <: Form # Product type
name::Symbol
factors::Vector{Form}
end
struct ColimitForm <: Form # Sum type
name::Symbol
summands::Vector{Form}
end
struct ListForm <: Form # Powerset type
name::Symbol
element_form::Form
end
# Standard musical forms
const PitchForm = SimpleForm(:Pitch, :Z)
const OnsetForm = SimpleForm(:Onset, :R)
const DurationForm = SimpleForm(:Duration, :R)
const LoudnessForm = SimpleForm(:Loudness, :R)
const NoteForm = LimitForm(:Note, [PitchForm, OnsetForm, DurationForm, LoudnessForm])
const ChordForm = ListForm(:Chord, NoteForm)
const ScoreForm = ListForm(:Score, ChordForm)
Denotators (Instances)
function Note(pitch::Int, onset::Float64, duration::Float64, loudness::Float64=0.8)
LimitDenotator(NoteForm, [
SimpleDenotator(PitchForm, pitch),
SimpleDenotator(OnsetForm, onset),
SimpleDenotator(DurationForm, duration),
SimpleDenotator(LoudnessForm, loudness)
])
end
function Chord(notes::Vector)
ListDenotator(ChordForm, notes)
end
Morphisms (Transformations)
struct TranspositionMorphism <: Morphism
semitones::Int
end
struct InversionMorphism <: Morphism
axis::Int
end
struct RetrogradeMotion <: Morphism end
struct AugmentationMorphism <: Morphism
factor::Float64
end
# Apply transposition
function apply(m::TranspositionMorphism, d::SimpleDenotator)
if d.form == PitchForm
SimpleDenotator(PitchForm, mod(d.value + m.semitones, 12))
else
d
end
end
Neo-Riemannian PLR Group
const P = PLROperation(:P) # Parallel: change third quality
const L = PLROperation(:L) # Leading-tone exchange
const R = PLROperation(:R) # Relative
function apply_plr(op::Symbol, triad::Vector{Int})
root, third, fifth = triad
if op == :P
# Major ↔ minor
if mod(third - root, 12) == 4
[root, mod(third - 1, 12), fifth]
else
[root, mod(third + 1, 12), fifth]
end
elseif op == :L
# Leading-tone exchange
if mod(third - root, 12) == 4
[mod(root - 1, 12), third, fifth]
else
[root, third, mod(fifth + 1, 12)]
end
elseif op == :R
# Relative major/minor
if mod(third - root, 12) == 4
[root, third, mod(fifth + 2, 12)]
else
[mod(root - 2, 12), third, fifth]
end
end
end
PLR Example
C Major [0, 4, 7]
P → c minor [0, 3, 7]
L → e minor [11, 4, 7] → [7, 11, 4] normalized
R → a minor [0, 4, 9] → [9, 0, 4] normalized
Gay.jl Color Integration
const NOTE_NAMES = ["C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B"]
function hue_to_pitch_class(hue::Float64)::Int
mod(round(Int, hue / 30.0), 12)
end
function pitch_class_to_hue(pc::Int)::Float64
mod(pc, 12) * 30.0 + 15.0
end
function color_to_note(color)::Int
rgb = convert(RGB, color)
hsl = convert(HSL, rgb)
hue_to_pitch_class(hsl.h)
end
# CatSharp trit mapping
function pitch_class_to_trit(pc::Int)::Int
pc = mod(pc, 12)
if pc ∈ [0, 4, 8] # Augmented
return 1
elseif pc ∈ [3, 6, 9] # Diminished
return 0
else
return -1
end
end
Tonnetz Navigation
struct Tonnetz
minor_third::Int # 3 semitones
major_third::Int # 4 semitones
fifth::Int # 7 semitones
end
const STANDARD_TONNETZ = Tonnetz(3, 4, 7)
function tonnetz_neighbors(pc::Int, t::Tonnetz=STANDARD_TONNETZ)
[
mod(pc + t.minor_third, 12),
mod(pc - t.minor_third, 12),
mod(pc + t.major_third, 12),
mod(pc - t.major_third, 12),
mod(pc + t.fifth, 12),
mod(pc - t.fifth, 12)
]
end
Klumpenhouwer Networks
struct KNet
nodes::Vector{Int}
arrows::Vector{Tuple{Int,Int,Symbol,Int}} # (from, to, T/I, n)
end
function verify_knet(knet::KNet)::Bool
for (from, to, op, n) in knet.arrows
pc_from = knet.nodes[from]
pc_to = knet.nodes[to]
expected = if op == :T
mod(pc_from + n, 12)
else # :I
mod(n - pc_from, 12)
end
if expected != pc_to
return false
end
end
true
end
GF(3) Triads
gay-mcp (-1) ⊗ catsharp-galois (0) ⊗ topos-of-music (+1) = 0 ✓
rubato-composer (-1) ⊗ ordered-locale (0) ⊗ topos-of-music (+1) = 0 ✓
Commands
# Run Topos of Music demo
julia dev/gadgets/topos_of_music.jl
# Apply PLR transformation
just plr-transform triad="0 4 7" op=P
# Navigate Tonnetz
just tonnetz-walk start=0 steps="m3 M3 P5"
# Verify K-net
just knet-verify nodes="0 4 7" arrows="T4 T3 T7"
Related Skills
catsharp-galois(0): Galois connection to Plurigridgay-mcp(-1): Color ↔ pitch mappingrubato-composer(-1): Rubato Composer integrationordered-locale(0): Frame structure for scales
References
- Mazzola, G. The Topos of Music (2002)
- Mazzola, G. Musical Performance (2011)
- Fiore & Noll. "Commuting Groups and the Topos of Triads"
- Cohn, R. "Neo-Riemannian Operations, Parsimonious Trichords"
More from plurigrid/asi
academic-research
Search academic papers across arXiv, PubMed, Semantic Scholar, bioRxiv, medRxiv, Google Scholar, and more. Get BibTeX citations, download PDFs, analyze citation networks. Use for literature reviews, finding papers, and academic research.
50wev-tesseract
WEV Tesseract Skill
33tree-sitter
AST-based code analysis using tree-sitter. Use for parsing code structure, extracting symbols, finding patterns with tree-sitter queries, analyzing complexity, and understanding code architecture. Supports Python, JavaScript, TypeScript, Go, Rust, C, C++, Swift, Java, Kotlin, Julia, and more.
22alife
Comprehensive Artificial Life skill combining ALIFE2025 proceedings, classic texts (Axelrod, Epstein-Axtell), ALIEN simulation, Lenia, NCA, swarm intelligence, and evolutionary computation. 337 pages extracted, 80+ papers, 153 figures.
16reverse-engineering
Reverse Engineering Skill
16bdd-mathematical-verification
BDD-Driven Mathematical Content Verification Skill
16