Lección 2: Embeddings OPTIC-K
Secciones
Ajustes de lectura
Ninguna voz para este idioma en este navegador.
Más voces
Código y términos técnicos en inglés
Desactivado: la voz de la página lo lee todo, sin pausa entre dos voces.
Ninguna voz para este idioma en este navegador.
Más voces
Más voces: Microsoft Edge ofrece voces naturales en línea, y Windows (Configuración › Hora e idioma › Voz), macOS y Android instalan otras.
Un embedding (vector de incrustación) es un array de números que describe un objeto, construido para que objetos parecidos reciban arrays parecidos. Los embeddings de texto salen de una red neuronal y nadie sabe decir qué significa la dimensión 417. OPTIC-K es lo contrario: cada uno de sus 240 números lo calcula código C# a partir de la teoría musical, y tiene nombre. Eso lo convierte en un buen primer embedding para estudiar, porque cada número se puede comprobar. Esta lección calcula los vectores de seis acordes con las propias clases de GA y los comprueba.
Todos los enlaces a GA apuntan al commit a826864. Las salidas proceden de:
dotnet run --project code/ga-ai/GaAi -c Release -- l2De dónde viene el nombre
Sección titulada «De dónde viene el nombre»En 2008, Clifton Callender, Ian Quinn y Dmitri Tymoczko describieron los objetos musicales por las equivalencias que uno decide ignorar: Octava, Permutación (el orden de las notas), Transposición, Inversión y Cardinalidad (notas duplicadas) (Generalized Voice-Leading Spaces). Si se ignoran la octava y el orden, un acorde se convierte en un conjunto de clases de altura; si se ignoran también la transposición y la inversión, se convierte en una clase de conjuntos, el tema de la lección 4 del curso de teoría musical. Una K, de complementariedad, viene de Harmonious, la referencia de acordes y escalas de Jared Updike: su página Equivalence Groups añade esa letra y aclara que no es terminología estándar. GA bautiza su embedding con la lista completa, y su clase OpticKClass cita esa página. Los comentarios de TheoryVectorService asocian cada bloque de números con las letras que debe cubrir.
El esquema
Sección titulada «El esquema»EmbeddingSchema.Partitions es el registro que todo consumidor debería leer: un nombre, un rango de las 240 posiciones, un peso y un rol para cada partición.
== EmbeddingSchema.Partitions (OPTIC-K-v1.8)partition raw dims role weightIDENTITY 0-5 6 Identity 0.00STRUCTURE 6-29 24 Similarity 0.45MORPHOLOGY 30-53 24 Similarity 0.25CONTEXT 54-65 12 Similarity 0.20SYMBOLIC 66-77 12 Similarity 0.10EXTENSIONS 78-95 18 Info 0.00SPECTRAL 96-108 13 Info 0.00MODAL 109-148 40 Similarity 0.10HIERARCHY 149-163 15 Info 0.00ATONAL_MODAL 164-227 64 Info 0.00ROOT 228-239 12 Similarity 0.05TotalDimension 240sum of dims 240sum of weights 1.15CompactDimension 124CompactLayoutV4 optk-v4-pp-r:STRUCTURE:0-23,MORPHOLOGY:24-47,CONTEXT:48-59,SYMBOLIC:60-71,MODAL:72-111,ROOT:112-123SchemaHashV4 0x37CD8ECFLo que describe cada partición, según el generador y sus servicios:
| Partición | Describe | Se calcula a partir de |
|---|---|---|
| STRUCTURE | qué clases de altura, cuántas, el vector interválico | el conjunto de notas, sin octava ni orden |
| MORPHOLOGY | la forma sobre el mástil: bajo y nota más aguda, extensión, posición en el mástil, cejilla | el voicing físico |
| CONTEXT | función armónica, tensión, movimiento | el análisis del acorde |
| SYMBOLIC | etiquetas como técnica y estilo | etiquetas del análisis |
| MODAL | cuánto sugiere el acorde cada modo: 40 posiciones, 33 de ellas con nombre de modo | las notas, respecto a una fundamental |
| ROOT | la clase de altura de la fundamental, en one-hot | la fundamental |
| IDENTITY, EXTENSIONS, SPECTRAL, HIERARCHY, ATONAL_MODAL | tipo de objeto, rasgos psicoacústicos y espectrales, complejidad, familias de la teoría de conjuntos | se guardan, nunca puntúan |
Tres cosas que observar:
- Solo se buscan las seis particiones “Similarity”. Juntas contienen 124 números, la
CompactDimension. Las otras cinco son información para otras herramientas; el archivo del índice ni siquiera las guarda (lección 3). - Los pesos suman 1,15, no 1. Por eso un vector comparado consigo mismo puntúa 1,15, como muestra la salida de abajo. Nada se rompe, ya que solo importa el orden, pero una puntuación no es un coseno en el sentido habitual de 0 a 1.
- La cadena de disposición se resume con un hash.
SchemaHashV4es el CRC-32 deCompactLayoutV4(líneas 154-186). La issue #616 indica0x37CD8ECFpara el índice en producción de GA, de 313.047 voicings: el curso y ese índice usan la misma disposición.
Junto al registro, EmbeddingSchema conserva constantes más antiguas para cada partición. Tres de ellas ya no coinciden:
== Loose constants next to the registryconstant value registryHierarchyDim 8 15AtonalModalDim 17 64ExtensionsEnd 96 240ExtensionsEnd es correcta, 96 es donde termina EXTENSIONS, pero su comentario dice que es igual a TotalDimension, lo que era cierto cuando el vector tenía 96 números. Las otras dos importan más: ModalVectorService reserva su array ATONAL_MODAL con AtonalModalDim, 17, y WriteInto copia la longitud que se le dé sin protestar, así que 47 de las 64 posiciones de esa partición nunca pueden ser otra cosa que cero. La lección 3 encuentra exactamente 47 en un corpus entero.
Versiones
Sección titulada «Versiones»El esquema creció añadiendo particiones al final. La historia de abajo procede de los documentos del esquema de GA, de los comentarios de versión de EmbeddingSchema.cs y de dos commits:
| Versión | Dimensiones | Qué cambió | Fuente |
|---|---|---|---|
| v1.2.1 | 96 | EXTENSIONS añadida; “indices 0–77 are unchanged” | OPTIC-K_Embedding_Schema_v1.2.1.md, fechado el 2026-01-11 |
| v1.3, v1.3.1 | 108, 109 | SPECTRAL, y luego la entropía espectral en el índice 108 | OPTIC-K_Embedding_Schema_v1.3.md, _v1.3.1.md |
| v1.4 | 216 | índices 0-135 definidos, el resto reservado | OPTIC-K_Embedding_Schema_v1.4.1.md, fechado el 2026-01-21 |
| v1.6 | MODAL y HIERARCHY | comentarios de EmbeddingSchema.cs |
|
| v1.7 | 228 | ATONAL_MODAL | comentarios de EmbeddingSchema.cs |
| v1.8 | 240 | ROOT añadida; se quita la bonificación de la fundamental en STRUCTURE | commit 95b5a9d9, 2026-04-19 |
El formato del índice tiene su propia historia: v4 normalizaba todo el vector compacto de una vez, v4-pp normaliza cada partición por separado (commit 3ba35365, 2026-04-19), y v4-pp-r añade ROOT, lo que llevó el tamaño compacto de 112 a 124.
La documentación no ha seguido el ritmo. El documento de esquema más reciente se queda en v1.4.1, y el que no lleva versión en el nombre, OPTIC-K_Embedding_Schema.md, describe v1.3.1 y 109 dimensiones. En el código, el resumen del generador todavía dice “228-dimensional canonical musical embedding (v1.7)” (línea 13) y su propiedad Dimension “216 for v1.4/v1.5” (línea 55), mientras que los dos devuelven 240. Cuando un comentario y el registro no coinciden, fíate del registro, y mejor aún, imprímelo.
De una forma de acorde a un documento
Sección titulada «De una forma de acorde a un documento»Los guitarristas escriben las formas de acorde de la cuerda E grave a la E aguda: x32010 es C abierto, con la E grave silenciada. GA numera las cuerdas al revés, desde la E aguda, la cuerda 1, y su generador de voicings construye las posiciones en ese orden. Voicings.FromShape, del curso, convierte un diagrama de acordes al orden de GA, y luego Voicings.Embed ejecuta los tres pasos que ejecutan las propias herramientas de GA:
var analysis = VoicingAnalyzer.Analyze(voicing);var doc = VoicingDocumentFactory.FromAnalysis(voicing, analysis, tuningId: "guitar");var raw = Generator.GenerateEmbeddingAsync(doc).GetAwaiter().GetResult();El paso del medio construye un ChordVoicingRagDocument, un record de unas cincuenta propiedades que lee el generador. Seis acordes:
== From a chord shape to GA's voicing documentshape GA diagram GA name pcs MIDI doc root inv rootless functionx32010 0-1-0-2-3-x C 0 4 7 64 60 55 52 48 4 E 1 yes Functional Harmonyx35553 3-5-5-5-3-x C 0 4 7 67 64 60 55 48 7 G 2 yes Functional Harmony032010 0-1-0-2-3-0 C/E 0 4 7 64 60 55 52 48 40 4 E 1 yes Functional Harmonyx32000 0-0-0-2-3-x Cmaj7 0 4 7 11 64 59 55 52 48 4 E 1 yes Functional Harmonyx02210 0-1-2-2-0-x Am 0 4 9 64 60 57 52 45 4 E 2 yes Functional Harmony320003 3-0-0-0-2-3 G 2 7 11 67 59 55 50 47 43 7 G 0 yes Functional HarmonyLos nombres son correctos. La fundamental del documento no: es E para C abierto y G para C con cejilla. VoicingDocumentFactory toma la primera nota MIDI a la vez como fundamental y como bajo:
RootPitchClass = analysis.MidiNotes.Length > 0 ? analysis.MidiNotes[0] % 12 : 0,MidiBassNote = analysis.MidiNotes.Length > 0 ? analysis.MidiNotes[0] : 0,Como las notas vienen empezando por la cuerda 1, MidiNotes[0] es la nota más aguda, la nota de la melodía, ni el bajo ni la fundamental. La columna de inversión también se calcula a partir de esa nota: C abierto en estado fundamental aparece como primera inversión. La fundamental con la que se compara la lee PitchClass.Parse, que toma los nombres de nota A y E por los números 10 y 11 (líneas 43-44): fuera del programa del curso, un Em en estado fundamental, 022000, sale con la inversión -1. Toda partición que lee RootPitchClass o MidiBassNote hereda el error: ROOT, MODAL y parte de MORPHOLOGY.
Dos records posicionales
Sección titulada «Dos records posicionales»La columna “rootless” dice que sí para los seis acordes, y todos contienen su fundamental. La causa es un patrón que todo desarrollador C# debería reconocer. VoicingCharacteristics es un record posicional de once parámetros, y VoicingHarmonicAnalyzer lo construye con argumentos posicionales:
// El record: (ChordId, DissonanceScore, Consonance, IntervalSpread, NoteCount,// IntervalClassVector, IsRootless, DropVoicing, IsOpenVoicing, Features, SemanticTags)return new( chordId, dissonanceScore, consonance, intervalSpread, pitchClasses.Count, pcSet.IntervalClassVector.ToString(), intervalSpread > 12, dropVoicing, false, [], semanticTags);intervalSpread > 12, “las notas abarcan más de una octava”, es lo que significa un voicing abierto, y acaba en IsRootless; la constante false acaba en IsOpenVoicing. El compilador no puede objetar nada: los dos son bool. Lo mismo ocurre un nivel más arriba, en VoicingAnalyzer, con un record declarado como PerceptualQualities(double Brightness, double ConsonanceScore, double Roughness, string TexturalDescription, string Register):
var perceptualQualities = new PerceptualQualities(curVoiceChars.Consonance, 0, 0, "Neutral", "Medium");La consonancia va a Brightness, y ConsonanceScore vale siempre 0. El programa imprime los dos records para los seis acordes:
== VoicingCharacteristics and PerceptualQualities, as VoicingAnalyzer fills themshape span IsRootless IsOpen Consonance Brightness ConsonanceScorex32010 16 True False 1.000 1.000 0.000x35553 19 True False 1.000 1.000 0.000032010 24 True False 1.000 1.000 0.000x32000 16 True False 1.000 1.000 0.000x02210 19 True False 1.000 1.000 0.000320003 24 True False 1.000 1.000 0.000El documento copia ConsonanceScore en su propiedad Consonance, y el generador calcula la tensión de CONTEXT como 1.0 - doc.Consonance (línea 102). Así que la tensión es 1 para todos los acordes indexados. La issue #616 midió la partición CONTEXT como “vacía”, con una dimensión constante, y culpó a los literales que se pasan al lado; la dimensión constante es esta, y su causa es el record de arriba. Con argumentos con nombre, new PerceptualQualities(Brightness: ..., ConsonanceScore: ...), los dos descuidos habrían sido visibles en la llamada.
Los 240 números de C abierto
Sección titulada «Los 240 números de C abierto»== The 240 values of x32010 (C), partition by partitionIDENTITY 3/6 1 0 1 0 0 1STRUCTURE 10/24 1 0 0 0 1 0 0 1 0 0 0 0 0.5 0 0 1 1 1 0 0 1 0.83 1 0MORPHOLOGY 7/24 0 0 0 0 1 0 0 0 0 0 0 0 0.17 0.83 1 0.87 -0.5 0.33 0 0 0 0 0 0CONTEXT 1/12 0 0 0 0 1 0 0 0 0 0 0 0SYMBOLIC 4/12 1 1 0 0 0 0 0 0 0 1 0 1EXTENSIONS 9/18 0 1 0.4 0 0 0.57 0.33 0.47 0.2 0 0 0 0.33 0 0 1 0.6 0SPECTRAL 13/13 0.2 0.26 0.61 0.37 0.4 0.47 0.29 0.6 0.53 0.29 0.64 0.5 0.16MODAL 2/40 0 0 0 0 0 0.43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.67 (+13 zeros)HIERARCHY 1/15 0.33 0 0 0 0 0 0 0 0 0 0 0 0 0 0ATONAL_MODAL 11/64 0.2 0.5 0 1 0 0 0.33 0.33 0.33 0 0.03 0.44 0.67 1 0 0.88 (+48 zeros)ROOT 1/12 0 0 0 0 1 0 0 0 0 0 0 0MODAL slots set: Aeolian=0.43, LydianAugmentedSharp2=0.67STRUCTURE se puede leer a mano con TheoryVectorService.ComputeEmbedding abierto al lado:
| Posiciones | Valores | Significado |
|---|---|---|
| 0-11 | 1 0 0 0 1 0 0 1 0 0 0 0 |
el croma: clases de altura 0, 4 y 7, C E G |
| 12 | 0.5 |
cardinalidad, 3 ÷ 12 × 2 |
| 13-18 | 0 0 1 1 1 0 |
el vector interválico <001110> de una tríada mayor |
| 19 | 0 |
complementariedad, siempre pasada como 0.0 |
| 20, 21 | 1, 0.83 |
consonancia y brillo derivados del vector |
| 22 | 1 |
“se conoce una fundamental” |
| 23 | 0 |
reservada |
La línea CONTEXT es la tensión constante de la sección anterior, en la posición 4 de ContextVectorService; las posiciones 6 a 11 están “reservadas” y nunca se escriben. La línea ROOT tiene su 1 en la clase de altura 4, E, la nota más aguda. Las posiciones MODAL también se calculan respecto a una fundamental, lo que explicaría por qué una tríada de C mayor enciende Aeolian, un modo menor: leídas desde E, C E G son E, G, C (una interpretación, por verificar línea a línea en ModalVectorService).
Nueve de las posiciones MODAL con nombre no las activa ninguno de los 15.360 voicings del corpus de la lección 3.
La similitud a mano
Sección titulada «La similitud a mano»GA compara dos vectores partición por partición: el coseno de cada partición de similitud, multiplicado por su peso, y todo sumado. WeightedPartitionCosine lo hace con TensorPrimitives.CosineSimilarity, y se salta una partición que sea toda ceros. El curso imprime cada coseno frente a C abierto:
== Cosine per partition, and GA's weighted score, against open Cpair structure morphology context symbolic modal root weighted compact dotx32010 x35553 1.000 0.348 1.000 1.000 0.000 0.000 0.837 0.837x32010 032010 1.000 0.997 1.000 0.750 1.000 1.000 1.124 1.124x32010 x32000 0.883 0.998 1.000 0.816 0.541 1.000 1.033 1.033x32010 x02210 0.888 0.998 1.000 0.816 0.153 1.000 0.996 0.996x32010 320003 0.776 0.496 1.000 0.671 0.000 0.000 0.740 0.740open C with itself: 1.150La primera fila, C abierto frente a C con cejilla en el tercer traste, a mano:
0.45 × 1.000 + 0.25 × 0.348 + 0.20 × 1.000 + 0.10 × 1.000 + 0.10 × 0.000 + 0.05 × 0.000 = 0.837
Leída musicalmente, la tabla sorprende. El C con cejilla tiene exactamente las notas de C abierto, y aun así puntúa menos que Am (0.996) y que Cmaj7 (1.033). Su STRUCTURE es idéntica; lo que lo hunde son ROOT y MODAL, ambas calculadas a partir de la nota más aguda, G en vez de E, y su forma de mano distinta. C abierto y C/E, que comparten sus cinco cuerdas superiores, puntúan 1.124. Y CONTEXT vale 1.000 en todas las filas: 0,20 de cada puntuación es una constante.
La última columna es el producto escalar de los dos vectores compactos. ExtractCompact conserva las seis particiones de similitud, divide cada una por su norma euclídea y la multiplica por la raíz cuadrada de su peso. Entonces, para una partición p, (√w · a/|a|) · (√w · b/|b|) = w · cos(a, b), y el producto escalar de dos vectores compactos es la puntuación ponderada. Por eso el índice guarda vectores compactos: un único producto escalar por voicing sustituye seis cosenos.
A qué es invariante el vector
Sección titulada «A qué es invariante el vector»El croma de STRUCTURE ignora octavas y orden de las notas: cualquier voicing de C E G recibe las mismas doce posiciones, y por eso el C con cejilla puntúa 1.000 ahí. La transposición es otra historia. El curso compara la STRUCTURE de C mayor con sus once transposiciones, y por separado las posiciones 12 a 18, la cardinalidad y el vector interválico:
== STRUCTURE of C major (0 4 7) against its 11 transpositions (TheoryVectorService)T pcs structure icv dims1 1 5 8 0.665 1.0002 2 6 9 0.665 1.0003 3 7 10 0.776 1.0004 4 8 11 0.776 1.0005 0 5 9 0.776 1.0006 1 6 10 0.665 1.0007 2 7 11 0.776 1.0008 0 3 8 0.776 1.0009 1 4 9 0.776 1.00010 2 5 10 0.665 1.00011 3 6 11 0.665 1.000set classes swept 222invariant (min cosine > 0.9999) 1mean of the minimum cosines 0.881worst minimum cosine 0.514Las dimensiones interválicas no se mueven; la partición completa sí, porque el croma nombra las notas literales. De todas las clases de conjuntos de dos notas o más, solo una es invariante, el conjunto cromático de doce notas, el único conjunto igual a todas sus transposiciones. Son las cifras del propio barrido de GA, state/quality/domain-invariants/README.md: “1 of 222 set classes is T-invariant”, media 0,88, peor 0,51. TheoryVectorService recoge el hallazgo en un comentario; RootVectorService sigue diciendo que sacar la fundamental hizo que STRUCTURE fuera “genuinely O+P+T+I-invariant”.
Así que OPTIC-K es invariante a la octava y al orden, como promete el nombre, y no a la transposición ni a la inversión: una búsqueda de “esta forma en otra tonalidad” no puede apoyarse en STRUCTURE.
Una herramienta MCP, dos órdenes de diagrama
Sección titulada «Una herramienta MCP, dos órdenes de diagrama»GA expone el generador a los asistentes de IA mediante una herramienta MCP, ga_generate_voicing_embedding. Su descripción promete “a 228-dim OPTIC-K embedding vector” y da el ejemplo 'x-3-2-0-1-0' for Cmaj7. Su parser lee el primer elemento como la cuerda 1, la E aguda, igual que el generador de GA. El curso copia ese parser y le pasa el ejemplo:
== ga_generate_voicing_embedding's example diagram, x-3-2-0-1-0parsed by GA diagram MIDI GA name pcsthe MCP tool's parser x-3-2-0-1-0 62 57 50 46 40 Dsus2/E 2 4 9 10chord chart x32010 0-1-0-2-3-x 64 60 55 52 48 C 0 4 7El ejemplo está escrito como un diagrama de acordes, empezando por la E grave, y se analiza empezando por la E aguda: un asistente que siga la descripción obtiene el vector de otro acorde. Leído empezando por la E grave sería C abierto, y seguiría sin ser Cmaj7. El curso de programación agéntica lo plantea en general: la descripción de una herramienta MCP forma parte de su interfaz, y el modelo se la cree.
Ejercicios
Sección titulada «Ejercicios»- Con la tabla de cosenos, calcula a mano la puntuación ponderada de C abierto frente a G (
320003), y compárala con la salida. - ¿Por qué C abierto frente a Am puntúa 1.000 en ROOT, si sus fundamentales son C y A?
- Los pesos suman 1,15. Sin cambiar nada del código, ¿cómo convertirías una puntuación de GA en un número entre 0 y 1, y cambiaría eso algún resultado de búsqueda?
- Reescribe la llamada a
PerceptualQualitiesdeVoicingAnalyzercon argumentos con nombre, respetando la intención evidente del autor.
Soluciones
0.45 × 0.776 + 0.25 × 0.496 + 0.20 × 1.000 + 0.10 × 0.671 + 0.10 × 0.000 + 0.05 × 0.000 = 0.3492 + 0.124 + 0.20 + 0.0671 = 0.740, el valor impreso.- Porque la fundamental del documento es la nota más aguda, y las dos formas tienen E arriba,
64en sus listas MIDI. El one-hot ROOT de ambas tiene su 1 en la clase de altura 4. - Dividir por 1,15, la suma de los pesos, o por la puntuación de un vector consigo mismo. No cambia ningún orden, porque todas las puntuaciones se dividen por la misma constante. Solo importa donde una puntuación se compara con un umbral fijo.
new PerceptualQualities(Brightness: 0, ConsonanceScore: curVoiceChars.Consonance, Roughness: 0, TexturalDescription: "Neutral", Register: "Medium"). “Respetar la intención” es una suposición: quizá el brillo también debía calcularse. Con argumentos con nombre, al menos la pregunta se ve en la revisión.
Puntos clave
Sección titulada «Puntos clave»- OPTIC-K es un embedding hecho a mano: 240 números con nombre en 11 particiones, de las que 6 particiones y 124 números sirven para la similitud, con pesos que suman 1,15.
- La similitud es una suma ponderada de cosenos por partición; guardar cada partición normalizada y escalada por la raíz cuadrada de su peso la convierte en un único producto escalar.
- El vector es invariante a la octava y al orden de las notas, no a la transposición: el croma de STRUCTURE nombra las notas.
- En el commit
a826864, la fundamental del documento es la nota más aguda, dos records posicionales ponen valores en el lugar equivocado, y CONTEXT es una constante: los vectores son deterministas, pero varias particiones no significan lo que dicen sus nombres. - Los comentarios y los documentos describen versiones antiguas (96, 109, 216, 228 dimensiones); el registro, y un programa que lo imprima, son la fuente fiable.
Fuentes
Sección titulada «Fuentes»- Clifton Callender, Ian Quinn y Dmitri Tymoczko, “Generalized Voice-Leading Spaces”, Science 320 (5874), 2008, pp. 346-348.
- GuitarAlchemist/ga en
a826864:Common/GA.Business.ML/Embeddings(EmbeddingSchema.cs,MusicalEmbeddingGenerator.cs,Services/TheoryVectorService.cs,Services/ContextVectorService.cs,Services/ModalVectorService.cs,Services/RootVectorService.cs),Common/GA.Business.ML/Rag/VoicingDocumentFactory.cs,Common/GA.Domain.Services/Fretboard/Voicings/Analysis(VoicingAnalyzer.cs,VoicingHarmonicAnalyzer.cs),Common/GA.Business.Core/Analysis/Voicings,Common/GA.Business.ML/Documentation/Schema,GaMcpServer/Tools/VoicingEmbeddingTool.cs,state/quality/domain-invariants/README.md; commits95b5a9d9y3ba35365. - Issue de GA #616, leída el 2026-09-14.
- Microsoft Learn: TensorPrimitives.CosineSimilarity, Records, Argumentos con nombre y opcionales, Crc32.
- Wikipedia, Similitud del coseno.