Unidad 4 · Las familias · el perfecto que corrige tres, su extendido auto-dual, las 759 octadas que cubren cada quinteto una vez, y por qué cuatro errores se detectan pero no se corrigen
Hamming resolvió el caso de un error, y lo resolvió con una familia entera: un código para cada cantidad de bits de paridad. Para más de un error, fuera de las repeticiones, hay una sola respuesta perfecta, y no es una familia sino un código suelto de 23 bits. Su versión de 24 es la que se usa, y trae adentro una estructura combinatoria que no tiene nada que ver con corregir errores y que, sin embargo, explica exactamente qué pasa cuando llegan cuatro.
El código de Golay es un código lineal binario . Con distancia mínima 7, su capacidad de corrección es
con once bits de paridad. Una matriz generadora tiene doce filas, y la más corta de escribir arma cada una corriendo la anterior un lugar. La primera es el vector de los exponentes de
o sea , con la posición 0 a la izquierda, y la fila es ese mismo vector desplazado lugares a la derecha: , para . Que un polinomio genere el código entero es la estructura cíclica del Golay, y con ella su decodificación eficiente: las dos son tema de la Unidad 5. Acá alcanza con la . El apunte menciona otras construcciones —por residuos cuadráticos módulo 23, desde el icosaedro, lexicográfica—, y todas dan el mismo código salvo una permutación de las posiciones.
Enumeradas las palabras, los pesos son éstos, los de la Figura 5 del apunte:
| código | 0 | 7 | 8 | 11 | 12 | 15 | 16 | 23 | 24 |
|---|---|---|---|---|---|---|---|---|---|
| (23, 12) | 1 | 253 | 506 | 1288 | 1288 | 506 | 253 | 1 | — |
| (24, 12) | 1 | 0 | 759 | 0 | 2576 | 0 | 759 | 0 | 1 |
Los dos renglones suman 4096. El primero no tiene ninguna palabra de peso entre 1 y 6, y por eso . El segundo es el extendido de la sección 03. Los dos son simétricos, , porque la palabra de todos unos está en los dos códigos: sumarla a una palabra de peso da una de peso .
Paso 1: contar una esfera. Alrededor de cada palabra, los vectores a distancia son la palabra misma, los 23 que difieren en un bit, los que difieren en dos y los que difieren en tres:
Paso 2: multiplicar por las palabras. Las esferas de radio 3 no se pisan, porque . Entre las esferas juntan vectores: todos los de largo 23. No queda ninguno afuera. Es la igualdad en la cota de Hamming, que es la definición de código perfecto.
Paso 3: leerlo con síndromes. Hay síndromes y exactamente 2048 patrones de peso , cada uno con su síndrome propio. Armada la tabla de decodificación, los líderes de coset son 1 de peso 0, 23 de peso 1, 253 de peso 2 y 1771 de peso 3: sin empates y sin síndromes libres. La decodificación por tabla del apunte es esa: 2048 entradas, una por cada patrón que el código promete corregir, y ni una más.
Que una igualdad así sólo se dé, con y fuera de las repeticiones, en este código —y que el cumpla la cuenta sin existir— está en las cotas de Singleton y de Hamming. Lo que importa acá es el precio. Con cuatro errores lo recibido cae, siempre, en la esfera de otra palabra: de los 8855 patrones de peso 4, los 8855 tienen un líder de peso 3 en su síndrome, y el decodificador entrega una palabra que está a distancia 7 de la enviada. Le pasa a todo código perfecto, y en el lo mide en un código perfecto, la bandera no se levanta.
A cada palabra se le agrega en la posición 23 un bit de paridad global, la suma de las otras 23. Es el código extendido, y el teorema de la extensión dice que una distancia impar sube en uno: las palabras de peso 7 ganan un 1 y pasan a peso 8, las de peso 8 quedan como estaban. Queda un , y el segundo renglón de la tabla de arriba es esa cuenta hecha: palabras de peso 8, de peso 12.
Ya no es perfecto. Con sigue corrigiendo 3, pero ahora sobra margen: , así que en modo mixto corrige 3 y detecta 4. El apunte da tres razones para preferirlo al perfecto: la tasa es exactamente , el largo es de tres octetos, y es auto-dual. La tercera es la que da casi todo lo demás.
Un código es auto-dual cuando es igual a su código dual: . La misma sirve de matriz de paridad.
Paso 1: las filas son ortogonales. Cada fila de es , de peso 7, más su paridad: peso 8, par, así que cada fila es ortogonal a sí misma. Y los 144 productos , contados todos los pares con incluidos, dan 0. Por linealidad cualquier par de palabras es ortogonal: .
Paso 2: las dimensiones cierran. El dual de un tiene dimensión , la misma que . Un subespacio contenido en otro de la misma dimensión es ese otro: . Por eso hace falta el bit de paridad: un código es auto-dual sólo si , y con largo impar eso no se puede.
Paso 3: todo peso es par. Toda palabra es ortogonal a sí misma, y es la suma de sus unos módulo 2: es par.
Paso 4: y múltiplo de 4. Con el vector que tiene un 1 donde y tienen un 1 los dos, el peso de una suma descuenta dos veces lo compartido:
Que y sean ortogonales es que sea par, y entonces el último término es múltiplo de 4. Si y tienen peso múltiplo de 4, su suma también. Las filas pesan 8, y toda palabra es una suma de filas: todas las palabras pesan 0, 4, 8, 12, 16, 20 o 24. De ésos, la tabla muestra que el 4 y el 20 no aparecen, porque .
Las 759 palabras de peso 8 se llaman octadas, y conviene mirarlas como conjuntos: las ocho posiciones donde valen 1. Forman un sistema de Steiner : subconjuntos de 8 elementos de un conjunto de 24 tales que cada subconjunto de 5 está en exactamente uno de ellos.
Paso 1: a lo sumo una. Si dos octadas distintas compartieran cinco posiciones, su suma, que también es palabra del código, tendría peso , menos que . Dos octadas comparten como mucho cuatro posiciones. Medido sobre los 287 661 pares: comparten 0, 2 o 4, y nunca otra cantidad.
Paso 2: contar quintetos. Cada octada contiene quintetos, y por el paso 1 ninguno se repite entre octadas. Entre todas cubren
quintetos distintos, que son todos los que hay. Cada uno está en una octada, y en una sola. La enumeración lo confirma uno por uno: los 42 504 quintetos, cada uno en exactamente una de las 759.
La misma cuenta, hecha con menos posiciones, dice cuántas octadas contienen posiciones dadas, y que el número no depende de cuáles: se cuentan los quintetos que extienden esas y se divide por los que caen en cada octada,
El widget deja hacerlo a mano antes de leer los valores.
Con una posición quedan 253 octadas; con dos, 77; con tres, 21; con cuatro, 5; con cinco, 1. Son , y con cada hasta 5 se recorrieron todos los subconjuntos de posiciones: el número no cambia en ninguno. El 253 es el del perfecto: las octadas con un 1 en la posición 23 son las palabras de peso 7 con su paridad agregada. Con cuatro elegidas pasa lo más interesante: las cinco octadas agregan cuatro posiciones cada una, y esas cinco tétradas no se pisan. Con las tuyas son seis tétradas que reparten las 24 posiciones. El quinto toque cae en una de ellas, y eso elige la octada.
El tiene síndromes. Los patrones de error de peso son , cada uno con un síndrome propio: son los que se corrigen. Los otros síndromes quedan para los 10 626 patrones de peso 4, que no comparten síndrome con ninguno de peso menor y se reparten de a seis: .
Los seis patrones de un mismo síndrome son las seis tétradas del widget. Dos patrones de peso 4 dan el mismo síndrome cuando su suma es una palabra. Esa suma pesa a lo sumo 8, y como no hay palabras no nulas más livianas, pesa exactamente 8: los dos patrones son disjuntos y juntos forman una octada. Las cuatro posiciones de un error pertenecen a 5 octadas, cada una aporta la tétrada complementaria, y las seis son igual de probables: todas pesan 4. Medido sobre los 10 626 patrones de peso 4, siempre son seis, siempre reparten las 24 posiciones y siempre comparten síndrome. El decodificador no tiene cómo elegir, y lo que sí puede hacer es reconocer que el síndrome no es de ningún patrón de peso y avisar. Es la decodificación incompleta, y en el extendido sí tiene dónde levantar la bandera: los 1771 síndromes que el perfecto no tenía.
Decodificar con una tabla de 4096 entradas es realizable, y el apunte lo deja ahí. La estructura de permite hacerlo sin tabla, con unas pocas pruebas de peso sobre vectores de doce bits, y la decodificación del por su estructura cíclica es de la Unidad 5.
Las dos distribuciones y el sistema de Steiner, desde y sin nada más. Las palabras se arman sumando filas de a una: después de la fila la lista tiene todas las combinaciones de las primeras . Lo de los comentarios es la salida de correrlo.
from itertools import combinations
from collections import Counter
g = [0, 2, 4, 5, 6, 10, 11] # g(X) = 1 + X² + X⁴ + X⁵ + X⁶ + X¹⁰ + X¹¹
filas = [sum(1 << (e + i) for e in g) for i in range(12)] # X^i g(X): 23 bits
def extender(v): # paridad global en la posición 23
return v | (bin(v).count('1') % 2) << 23
palabras = [0]
for f in filas: # todas las sumas de filas de G
palabras += [p ^ f for p in palabras]
c24 = [extender(v) for v in palabras]
print(sorted(Counter(bin(v).count('1') for v in palabras).items()))
# [(0, 1), (7, 253), (8, 506), (11, 1288), (12, 1288), (15, 506), (16, 253), (23, 1)]
print(sorted(Counter(bin(v).count('1') for v in c24).items()))
# [(0, 1), (8, 759), (12, 2576), (16, 759), (24, 1)]
octadas = [v for v in c24 if bin(v).count('1') == 8]
cubre = Counter()
for o in octadas:
pos = [i for i in range(24) if o >> i & 1]
cubre.update(combinations(pos, 5))
print(len(cubre), set(cubre.values()))
# 42504 {1}
La última línea es el sistema de Steiner: 42 504 quintetos distintos aparecen, y cada uno aparece una vez. El bit del entero es la posición , así que el bit 23 es la paridad, la última posición.
El apunte y Lin y Costello (§4.6) ubican al en las sondas Voyager 1 y 2, en la transmisión de las imágenes en color de Júpiter y Saturno, entre 1979 y 1981. Años antes, Mariner 9 había usado un Reed-Muller , un que corrige 7 con tasa : por cada bit de imagen, más de cinco por el canal. El Golay manda dos.
Lo que no tiene es una familia. Un código de Hamming existe para cada , y un Reed-Muller para cada . El Golay es uno solo, con un largo y una tasa fijos: si un enlace pide corregir tres en otro largo, el Golay no tiene un hermano para ofrecer. Esa ausencia de un método general, que dado y dé un código y un decodificador, es con lo que cierra la unidad.