Circle STARKs es un nuevo esquema zk-STARK que ha sido implementado en Stwo y Plonky3, y ha sido adoptado por varios proyectos de zkVM. Su innovación clave radica en permitir el uso de pequeños campos de 32 bits (primo de Mersenne ) mientras mantiene las propiedades matemáticas necesarias para realizar operaciones eficientes de FFT. En esta serie de artículos explicativos, discutiremos el algoritmo principal detrás de Circle STARK, denominado Circle FFT.
En el artículo de la Parte 1, primero revisamos cómo han evolucionado los primos aptos para STARK, y luego exploramos los conceptos fundamentales de Circle FFT—es decir, la curva del círculo, su estructura de grupo y los roles de los twin-cosets y los cosets en posición estándar, utilizando ejemplos y derivaciones detalladas. Luego, en el artículo de la Parte 2, describiremos en detalle el algoritmo Circle FFT en sí.
También proporcionamos un recorrido por estas ideas centrales—como el grupo del círculo y la Circle FFT—junto con cálculos de ejemplo explícitos para (primo de Mersenne con exponente : ) con un script de Python.
Nuestro enfoque es cómo cada uno de estos bloques de construcción conduce a una rutina completa tipo FFT incluso cuando no tiene un factor 2-ádico grande.
Si estás interesado en ejecutar los ejemplos por ti mismo, el código completo de Python está disponible aquí, y lo referenciamos en secciones posteriores como la Sección 4 y 6, donde recorremos la construcción del grupo y del dominio en el código.
Este artículo fue escrito por Yugo en colaboración con RareSkills. Este artículo fue financiado por un Soulforge Grant. Agradecemos su apoyo.
0. De Primos Aptos para STARK a Mersenne 31
Antes de sumergirnos en los primos aptos para STARK, primero recordemos los conceptos básicos de los campos finitos.
Un campo es un conjunto en el que se definen la suma, resta, multiplicación y división (excepto por cero). Cuando el conjunto de elementos es finito, se le llama campo finito. se refiere al conjunto de enteros desde hasta (donde es un número primo), con la suma, resta y multiplicación tomadas módulo , y la división definida como la inversa de la multiplicación módulo .
Denotamos mediante al conjunto . Cada elemento en tiene un inverso multiplicativo, por lo que puede verse como un grupo multiplicativo. El tamaño de este grupo, , es . Cuando factorizamos en sus factores primos, se sabe que si un primo aparece veces, entonces hay un subgrupo cíclico de tamaño .
Puedes ilustrar esto brevemente con un primo pequeño, por ejemplo . Dado que se factoriza como , deben existir subgrupos de tamaños y . De hecho, en , el elemento genera un subgrupo de tamaño , y el elemento genera un subgrupo de tamaño .
En muchos protocolos STARK, la Transformada Teórica de Números (NTT) o Transformada Rápida de Fourier (FFT) depende de dicho subgrupo donde el factor es una potencia de 2, de modo que el subgrupo tiene tamaño . El mayor número entero tal que divide a se llama la 2-adicidad. Una alta 2-adicidad permite un mayor tamaño de potencia de 2 para los dominios de FFT/NTT, lo cual es clave para lograr evaluaciones, interpolaciones y multiplicaciones de polinomios rápidas. Además, implementar estas operaciones basadas en NTT de manera eficiente depende en gran medida de multiplicaciones modulares rápidas—ya que la FFT/NTT implica multiplicar repetidamente elementos y reducirlos módulo .
Originalmente, los STARKs utilizaban un primo , que tiene una 2-adicidad de . En las implementaciones modernas de STARK, una de las mejoras que se hizo fue reducir la sobrecarga disminuyendo el tamaño del campo finito. Por ejemplo, el campo Goldilocks es y tiene una 2-adicidad de . De manera crucial, debido a que el producto de dos números de 64 bits puede dividirse en base y reducirse con solo unas pocas sumas y restas.
¿Qué hay de los Circle STARKs? Como se propone en el documento de Circle STARKs, utiliza un primo de Mersenne,
Un primo de Mersenne es un número primo de la forma para algún entero . Aquí, da , que resulta ser primo y por lo tanto está categorizado como un primo de Mersenne.
Una de las razones principales para elegir es que cabe dentro de una palabra de máquina de 32 bits, lo que hace que la multiplicación modular sea extremadamente rápida. Específicamente, cuando se multiplican dos números de 32 bits, el resultado es un valor de 64 bits, y la reducción modular por se puede realizar con solo dos sumas.
Esta operación puede ser mucho más rápida con instrucciones vectorizadas. Las CPUs modernas sobresalen procesando enteros de 32 bits nativamente, y el Mersenne 31 () se ajusta a esta arquitectura, permitiendo una utilización óptima de las capacidades del hardware. Comparado con el primo de 256 bits utilizado en el STARK original, el Mersenne 31 ofrece una multiplicación modular aproximadamente 125 veces más rápida, y un aumento de velocidad de 1.3 veces sobre Baby Bear (), como se discute en Why I’m Excited By Circle Stark And Stwo por Eli Ben-Sasson.
Sin embargo, el enfoque tradicional de FFT o NTT requiere un gran factor 2-ádico en para formar un subgrupo suficiente de potencia de 2 en . Aquí, el cual tiene una 2-adicidad de solo 1. Por lo tanto, no podemos usar directamente un subgrupo grande de potencia de 2 bajo la multiplicación.
Circle STARK todavía emplea el campo primo de Mersenne . Sin embargo, en lugar de trabajar directamente con en sí, observamos que la curva
Esto es crucial porque puede incluir una gran potencia de 2, creando subgrupos de tamaño . Por lo tanto, en lugar de trabajar con elementos individuales en , Circle STARK considera pares que satisfacen (es decir, puntos en la curva del círculo).
Al hacerlo, Circle STARK puede implementar su propia interpolación y evaluación rápida de polinomios—a menudo referida como la Circle FFT—directamente sobre estos elementos del círculo.
1. Curva del Círculo
Pasemos a las matemáticas centrales de Circle STARKs. Una curva del círculo sobre un campo finito es el subconjunto de definido por todos los puntos que satisfacen
A veces denotamos este conjunto por o simplemente “el círculo”.
En los Circle STARKs, nos centramos en primos con (por ejemplo, ). Bajo esta condición, no es un cuadrado en . Concretamente, esto asegura que la ecuación tenga exactamente soluciones en y ninguna solución atípica adicional (es decir, puntos en el infinito). Para este artículo, todo lo que necesitamos saber es que en produce exactamente puntos bajo esa condición.
(Sin embargo, para los lectores interesados en una perspectiva geométrica sobre por qué lleva a exactamente soluciones, ver el Apéndice).
1.1 Ejemplo de Juguete
Por ejemplo, si (que es ), entonces es el conjunto de todos los tales que
Aquí hay algunos pares en que satisfacen . Por ejemplo:
- y son obvios porque y .
- también funciona porque , y .
Vemos que hay exactamente pares de este tipo para .
2. Grupo del Círculo
Este tipo de conjunto se denota por y a menudo se llama Curva del Círculo sobre . Un hecho sorprendente es que . Por ejemplo, si , entonces hay exactamente puntos en el círculo — notablemente .
Esto es importante porque en el entorno usual de STARK, a menudo queremos un dominio de tamaño para operaciones similares a FFT. En el caso de , tener exactamente 8 puntos en el círculo significa que podemos formar subgrupos de tamaño . Más adelante, veremos cómo esta propiedad (la alta 2-adicidad en ) se explota en la Circle FFT.
Una propiedad clave es que se le puede dar a una operación de grupo (la cual es, en esencia, un operador binario) en sus puntos. Específicamente, usamos “” para denotar esta operación: para dos puntos y en ,
Podemos verificar que esta operación de grupo se mantiene dentro de (el resultado aún satisface ) y convierte el conjunto en un grupo cíclico de tamaño .
2.1 Verificación de los Axiomas de Grupo
Como los axiomas de un grupo, típicamente enumeramos las siguientes cuatro propiedades: clausura, el elemento identidad, el elemento inverso y la asociatividad. Echemos un vistazo más de cerca a cada una para confirmar que todas se cumplen.
2.1.1 Clausura
Toma dos puntos y en el círculo;
Define un nuevo punto
Mostraremos que también se encuentra en el círculo calculando paso a paso:
Dado que sabemos que y , sustituimos estos valores en el producto:
Por lo tanto también satisface y, en consecuencia, se encuentra en el círculo. Esto confirma que el conjunto es cerrado bajo la operación
2.1.2 Elemento Identidad
El punto sirve como el elemento identidad bajo la operación de grupo. Concretamente, para cualquier en el círculo,
Uno podría preguntarse sobre el punto . Si intentamos usar como una identidad, vemos que falla:
lo cual no es igual a para un general. Debido a que el elemento identidad es único, no puede ser la identidad.
2.1.3 Inverso
En un grupo, el inverso de un elemento es el punto único que satisface
En el círculo, vemos que sirve como el inverso de con respecto a la operación de grupo. De hecho,
ya que .
Por lo tanto, el inverso de bajo la operación de grupo es .
2.1.4 Asociatividad
El último axioma de grupo es la asociatividad: para cualesquiera tres puntos
en el círculo,
Esto puede verificarse expandiendo el polinomio, pero no entraremos en detalles aquí ya que sería demasiado extenso.
2.2 Una Operación Especial: El Mapeo de Cuadratura
Más allá de estos axiomas de grupo, hay otra operación en el círculo que es particularmente útil en secciones posteriores, especialmente para la Circle FFT, a saber, el mapeo de cuadratura. Este mapeo aplica la operación de grupo a un punto consigo mismo:
Dado que el círculo satisface , obtenemos , por lo que
En la Circle FFT, usaremos para reducir a la mitad ciertos dominios de evaluación de manera recursiva—cada aplicación de reduce el tamaño del dominio por un factor de 2. Esto se debe a que es compatible con la operación de grupo. Concretamente, para dos puntos y en el círculo,
Esta compatibilidad implica que mapea un twin-coset de tamaño en otro twin-coset de tamaño . Volveremos a esta propiedad de reducción a la mitad del dominio en detalle más adelante.
2.3 Una Operación Especial: La Involución
Además del mapeo de cuadratura, existe otra operación importante. Esta operación, llamada la involución, se define por
Geométricamente, invierte el signo de la coordenada dejando inalterada la coordenada . Nota que es su propia inversa—aplicar dos veces te devuelve al punto original:
En el círculo , la involución mantiene todos los puntos en la curva (ya que negar no afecta a ). Sin embargo, un punto con permanece fijo bajo , lo que significa que .
2.4 Operación Concreta del Grupo del Círculo en
En el ejemplo concreto anterior, exploramos con , que sirvió como un útil ejemplo de juguete para construir intuición. Sin embargo, es demasiado pequeño para revelar las propiedades estructurales más profundas del Grupo del Círculo—como cadenas de subgrupos más largas o la construcción de twin-cosets o cosets en posición estándar.
Por lo tanto, ahora pasamos a un primo ligeramente más grande, (que satisface ), para ilustrar estos comportamientos más ricos de una manera más significativa.
Dado , que es congruente con , el conjunto de puntos que satisfacen la ecuación , denotado como , se ilustra en el siguiente diagrama:

Aquí hay algunos ejemplos de pares en que satisfacen :
- también funciona porque , y .
- también es una solución: (mod 31) y (mod 31), por lo que (mod 31).
- demuestra que , por lo tanto (mod 31).
Para ver el Grupo del Círculo, trabajemos sobre , donde el círculo tiene 32 puntos. Supongamos que elegimos el punto
Podemos combinar con el punto a través de la operación de grupo:
En efecto, uno verifica
por lo que también yace en el círculo .
Mientras tanto, el inverso de es
porque
Adicionalmente, el mapeo de cuadratura puede funcionar en el punto . Recuerda que
Por lo tanto,
3. Subgrupos del Grupo del Círculo
Hemos establecido que es un grupo cíclico de tamaño . Si es una potencia de dos, digamos , entonces existe una cadena anidada de subgrupos
donde cada tiene un orden .
En otras palabras, es un subgrupo de tamaño 2, es de tamaño 4, y así sucesivamente, hasta en sí mismo, que tiene un tamaño de .
Por ejemplo, si (así que ), obtenemos una cadena
donde y cada tiene tamaño . Un resumen podría verse así:
| 1 | |
| 2 | |
| 4 | |
| 8 | |
| 16 | |
| 32 |
A continuación, listamos explícitamente cada subgrupo en .
Size 1: [Point(1, 0)]
Size 2: [Point(1, 0), Point(30, 0)]
Size 4: [Point(1, 0), Point(0, 30), Point(30, 0), Point(0, 1)]
Size 8: [Point(1, 0), Point(4, 27), Point(0, 30), Point(27, 27), Point(30, 0), Point(27, 4), Point(0, 1), Point(4, 4)]
Size 16: [Point(1, 0), Point(7, 13), Point(4, 27), Point(18, 24), Point(0, 30), Point(13, 24), Point(27, 27), Point(24, 13), Point(30, 0), Point(24, 18), Point(27, 4), Point(13, 7), Point(0, 1), Point(18, 7), Point(4, 4), Point(7, 18)]
Size 32: [Point(1, 0), Point(2, 11), Point(7, 13), Point(26, 10), Point(4, 27), Point(21, 5), Point(18, 24), Point(20, 29), Point(0, 30), Point(11, 29), Point(13, 24), Point(10, 5), Point(27, 27), Point(5, 10), Point(24, 13), Point(29, 11), Point(30, 0), Point(29, 20), Point(24, 18), Point(5, 21), Point(27, 4), Point(10, 26), Point(13, 7), Point(11, 2), Point(0, 1), Point(20, 2), Point(18, 7), Point(21, 26), Point(4, 4), Point(26, 21), Point(7, 18), Point(2, 20)]
4. Código Python 1: Grupo del Círculo
Ahora revisaremos el código del Grupo del Círculo a través de una implementación sencilla en Python. Solo se describen las funciones o partes más importantes aquí, y todo el código está disponible aquí.
Aquí, definimos dos clases clave — FieldElement y CirclePoint — para manejar la aritmética en el campo finito subyacente y las operaciones de grupo en la curva, respectivamente.
4.1 FieldElement
Primero, la clase FieldElement define las operaciones aritméticas en el campo finito , tales como la suma, multiplicación e inversión módulo 31. Esta es la base para todos los cálculos en la curva del círculo.
# Mersenne 5
MOD = 31
class FieldElement:
def __init__(self, value):
"""Initialize a field element"""
self.value = value % MOD
def __add__(self, other):
"""Add two field elements"""
return FieldElement((self.value + other.value) % MOD)
def __mul__(self, other):
"""Multiply two field elements"""
return FieldElement((self.value * other.value) % MOD)
def inv(self):
"""Compute the multiplicative inverse"""
return FieldElement(pow(self.value, MOD - 2, MOD))
def square(self):
"""Compute the square"""
return self * self
4.2 CirclePoint
La clase CirclePoint representa puntos en la curva del círculo . El método add implementa la operación de grupo, mientras que double aplica el mapeo de cuadratura.
class CirclePoint:
def __init__(self, x, y):
"""Initialize a point on the circle x^2 + y^2 = 1"""
if (x.square() + y.square()).value != 1:
raise ValueError("Point does not lie on the circle")
self.x = x
self.y = y
def add(self, other):
"""Perform group operation: (x1,y1)・(x2,y2) = (x1*x2 - y1*y2, x1*y2 + x2*y1)."""
nx = self.x * other.x - self.y * other.y
ny = self.x * other.y + other.x * self.y
return CirclePoint(nx, ny)
def double(self):
"""Apply squaring map: π(x,y) = (2*x^2 - 1, 2*x*y), since x^2 + y^2 = 1."""
xx = self.x.square().double() - FieldElement.one()
yy = self.x.double() * self.y
return CirclePoint(xx, yy)
@classmethod
def identity(cls):
"""Return the identity element (1, 0) of the circle group."""
return cls(FieldElement.one(), FieldElement.zero())
Luego puedes simular un ejemplo de la operación del Grupo del Círculo tal como se describe en la sección 2.4.
Por ejemplo, sumar el CirclePoint y el CirclePoint resulta en el Point .
p1 = CirclePoint(FieldElement(13), FieldElement(7))
p2 = CirclePoint(FieldElement(30), FieldElement(0))
# group operation
p3 = p1.add(p2)
print(f"p1・p2 = {p3}")
p1・p2 = Point(18, 24)
Duplicar da como resultado
# squaring map
p1_double = p1.double()
print(f"π(p1) = {p1_double}")
π(p1) = Point(27, 27)
El inverso de es , y sumarlos da la identidad
# Inverse
p1_inv = p1.inverse()
print(f"p1's inverse = {p1_inv}")
p1_plus_inv = p1.add(p1_inv)
print(f"p1・p1_inv = {p1_plus_inv}")
p1's inverse = Point(13, 24)
p1・p1_inv = Point(1, 0)
4.3 Grupo del Círculo
La función generate_subgroup genera un subgrupo de orden . Obtiene el generador apropiado de la función get_nth_generator y repite la operación de grupo add para construir el subgrupo.
def generate_subgroup(k: int) -> list[CirclePoint]:
"""Generate a subgroup of size 2^k using the generator."""
g_k = get_nth_generator(k)
p = CirclePoint.identity()
return [ (p := p.add(g_k)) if i else p
for i in range(1 << k) ]
Por ejemplo, puedes obtener el subgrupo de tamaño 8.
G3 = generate_subgroup(3)
Size 8: [Point(1, 0), Point(4, 27), Point(0, 30), Point(27, 27), Point(30, 0), Point(27, 4), Point(0, 1), Point(4, 4)]
5. Cosets, Twin-Cosets y Cosets en Posición Estándar
Los Twin-Cosets y los Cosets en Posición Estándar son los dominios en Circle STARKs. Revisemos brevemente cómo se utilizan los dominios en los STARKS tradicionales antes de entender las propiedades matemáticas de los twin-cosets y los cosets en posición estándar. Como se discutió en la Sección 0, los STARKS tradicionales típicamente utilizaban (sub)grupos multiplicativos representados como como sus dominios. Estos dominios servían principalmente para dos propósitos.
En primer lugar, actuaban como el dominio de evaluación al construir polinomios a partir de la traza computacional vía IFFT, y tenemos que realizar la Extensión de Bajo Grado (LDE) con el dominio extendido vía FFT, y además construimos polinomios de restricciones y los evaluamos sobre la LDE.
En segundo lugar, en la Prueba de Bajo Grado, particularmente con los compromisos de FRI, los polinomios deben ser evaluados vía FFT sobre dominios que se reducen iterativamente a la mitad durante los pasos recursivos de plegado de FRI.
Además, durante los pasos de plegado de FRI, a medida que el grado del polinomio se reducía a la mitad, el tamaño del dominio también necesitaba reducirse a la mitad. Reducir a la mitad el tamaño del dominio es fundamental tanto para FRI como para FFT. Ten esto en cuenta a medida que avanzamos.
Porque si bien el grupo del círculo en sí ya ofrece una alta 2-adicidad, la construcción de Twin-Cosets o Cosets en Posición Estándar asegura que, durante los pasos de cuadratura recursiva en la FFT, los dominios de evaluación (es decir, los Twin-Cosets o Cosets en Posición Estándar) también puedan ser reducidos a la mitad en tamaño mientras preservan su estructura de coset. Esto es esencial para permitir operaciones tipo FFT sobre estos dominios en cada nivel de la recursión.
5.1 Coset
Recordemos la definición de un coset en la teoría de grupos. Supongamos que es un subgrupo de un grupo , y sea . Entonces el coset izquierdo de por es el conjunto
Si , entonces . De lo contrario, en el caso de , es un conjunto disjunto de , pero aún tiene el mismo tamaño que . Esto se cumple porque si , la clausura de bajo la operación de grupo asegura que , mientras que si , ningún elemento de puede estar en sin implicar , y la biyección preserva el tamaño.
En nuestro entorno particular, , que es cíclico de tamaño . De la sección anterior, sabemos que hay una cadena de subgrupos con .
Por lo tanto, por ejemplo, si fijamos de orden , entonces para cualquier punto en el círculo, el coset
se llama un coset de .
Ese conjunto tendrá la misma cardinalidad , y es disjunto de en sí mismo a menos que ya pertenezca a .
5.1.1 Ejemplo de Coset
Vamos a ilustrar este ejemplo rápidamente. En el caso , recuerda que , por lo que existe una cadena de subgrupos donde . Concretamente,
Ahora tomemos un punto que no está en . Por ejemplo,
Dado que no está en , el conjunto
será un coset distinto de , disjunto de mismo.
Por lo tanto,
lo cual efectivamente tiene un tamaño de 2. Este es el coset de correspondiente a , claramente diferente del subgrupo mismo.
5.2 Twin-Cosets
Ahora definimos un twin-coset tomando la unión de dos cosets: y su coset inverso . Concretamente, sea
Decimos que es un twin-coset de tamaño si se cumplen las siguientes condiciones:
-
Disyunción
Los dos cosets y son disjuntos.
En la práctica, esta disyunción equivale a asegurar que . Esta equivalencia surge de las propiedades de los cosets. Si , entonces estaría tanto en como en (solapamiento). A la inversa, si los cosets no se solapan, entonces no se puede escribir como (para ningún ), lo que implica . -
Sin puntos fijos bajo la involución
Un punto se llama punto fijo de un mapeo si . En nuestro caso, consideramos la involución
Si contuviera algún punto donde , entonces permanecería inalterado. Es un punto fijo. Queremos evitar tener tales puntos en , porque por definición un twin-coset excluye cualquier punto fijo de .
Bajo estas condiciones, cada coset y tiene puntos distintos, dando un total de puntos en . Intuitivamente, fusionamos un coset con su coset inverso, formando un dominio de elementos.
5.2.1 Ejemplo de Twin-Coset
Continuando con el ejemplo del coset de la Sección 5.1.1 (donde escogimos que no está en ), formemos ahora un twin-coset de tamaño . Ya vimos que:
A continuación, calculemos y formemos el coset :
Por lo tanto,
Tomando la unión, el twin-coset es:
Este dominio tiene un tamaño de 4 y satisface las condiciones del twin-coset: los dos cosets son disjuntos (ya que ), y no contiene ningún punto con , por lo que no hay puntos fijos bajo la involución . Así, es en efecto un twin-coset de tamaño .
5.3 Cosets en Posición Estándar
Un coset en posición estándar es un tipo especial de twin-coset de tamaño que también coincide con un único coset del subgrupo :
Aquí, recuerda que es el único subgrupo de de orden , como se introdujo anteriormente en la Sección 3. Tenemos una cadena de subgrupos:
y cada es de tamaño . Entonces, contiene a , y relacionaremos los cosets de estos subgrupos usando un punto de orden .
Desglosemos esto paso a paso:
Primero, dado que , podemos ver a como compuesto por dos cosets disjuntos de , a saber:
Si ahora multiplicamos esto por un punto fijo de orden , obtenemos:
Resulta que , por lo tanto:
Por consiguiente, el coset se puede escribir como la unión de dos cosets disjuntos: uno por , el otro por , cada uno sobre el subgrupo más pequeño . Esta es exactamente la definición de un twin-coset.
Pero no todos los twin-cosets surgen de esta manera. Para asegurar que esta construcción funcione correctamente, requerimos:
- Disyunción: . De lo contrario, los dos cosets se solaparían.
- Sin puntos fijos bajo la involución: El conjunto resultante debe evitar los puntos con , para que la involución no tenga puntos fijos en .
- Orden correcto: debe tener orden para garantizar que abarque exactamente elementos distintos.
Cuando se satisfacen estos requisitos, el coset proporciona un coset en posición estándar: un dominio de tamaño que es simultáneamente un twin-coset y un solo coset de un subgrupo más grande.
5.4 Ejemplo Calculado a Mano: Twin-Cosets y Cosets en Posición Estándar en
A continuación, ilustramos cómo se aplican las nociones de twin-cosets y cosets en posición estándar cuando Tenemos , por lo que existen los subgrupos , donde y . Mostraremos el coset en posición estándar para que es .
5.4.1 Construcción del Twin-Coset
-
Configuración
Sea . Entonces tiene tamaño 4. Escoge un punto de orden 16 (por lo tanto , pero ). Concretamente, uno puede elegir -
Formando el Twin-Coset
Puesto que tiene los cuatro puntos , , , y , cada punto se multiplica por o .
- ,
- ,
- ,
- ,
- ,
- ,
- ,
- .
Combinar estos produce 8 puntos distintos. Ningún elemento de está fijo por , por lo tanto es un twin-coset de tamaño . Combinar estos produce 8 puntos distintos. Ningún elemento de está fijo por , por lo tanto es un twin-coset de tamaño .
En el diagrama a continuación, los puntos rojos 🔴 representan y los puntos azules 🔵 representan . Juntos, forman el twin-coset de 8 puntos.

5.4.2 Verificación del Coset en Posición Estándar
Este mismo también es igual a para algún subgrupo de tamaño 8, lo que significa que es un coset en posición estándar.
es el subgrupo , entonces multiplicar cada elemento por produce exactamente los 8 puntos en .
Por lo tanto
Debido a que es tanto un twin-coset de como un coset único de , lo llamamos un coset en posición estándar de tamaño 8.
En el diagrama a continuación, los puntos verdes 🟢 representan el subgrupo , y los puntos rojos 🔴 y azules 🔵 representan los dos cosets disjuntos y , respectivamente. Su unión forma el twin-coset , y el conjunto completo de 8 puntos constituye el coset en posición estándar .

Por lo tanto, siempre que tenga un orden de , podemos construir un coset en posición estándar de tamaño . Esta estructura es importante en Circle STARK, ya que proporciona un dominio limpio de puntos incluso si no es lo suficientemente 2-ádico.
6. Código Python 2: Dominio del Círculo
Ahora revisaremos el código del Dominio del Círculo a través de una implementación sencilla en Python. La construcción del dominio (tal como twin-coset y el coset en posición estándar) también está implementada en el mismo notebook de Python. Puedes ejecutar e inspeccionar cómo se construyen estos conjuntos paso a paso.
6.1 Twin-Coset
Esta función genera un dominio aplicando las operaciones de grupo add a y su inverso sobre el subgrupo, asegurando simetría y un tamaño .
def generate_twin_coset(Q, G_n_minus_1):
"""Generate twin-coset: D = Q・G_(n-1) ∪ Q^{-1}・G_(n-1)."""
Q_inv = Q.inverse()
coset_Q = [Q.add(g) for g in G_n_minus_1]
coset_Q_inv = [Q_inv.add(g) for g in G_n_minus_1]
return coset_Q + coset_Q_inv
G2 = generate_subgroup(2)
Q = CirclePoint(FieldElement(13), FieldElement(7))
twin_cosets = generate_twin_coset(Q, G2)
print(twin_cosets)
[Point(13, 7), Point(7, 18), Point(18, 24), Point(24, 13), Point(13, 24), Point(24, 18), Point(18, 7), Point(7, 13)]
6.2 Coset en Posición Estándar
Esta función genera el coset en posición estándar aplicando la operación de grupo add para y el subgrupo.
def generate_standard_position_coset(Q, G_n):
"""Generate standard position coset D = Q・G_n."""
return [Q.add(g) for g in G_n]
G3 = generate_subgroup(3)
Q = CirclePoint(FieldElement(13), FieldElement(7))
D = generate_standard_position_coset(Q, G3)
[Point(13, 7), Point(18, 7), Point(7, 18), Point(7, 13), Point(18, 24), Point(13, 24), Point(24, 13), Point(24, 18)]
Con este tipo de código, puedes comprobar la igualdad del coset en posición estándar
D_std = generate_standard_position_coset(Q, G3)
D_twin = generate_twin_coset(Q, G2)
print("Q・G3:", D_std)
print("(Q・G2) ∪ (Q^-1・G2):", D_twin)
if set(D_std) == set(D_twin):
print("Equal: standard position coset Q·G3 = Q·G2 ∪ Q^-1·G2")
else:
print("Not Equal")
7. Descomposición de un Coset en Posición Estándar Mayor en Twin-Cosets Menores
En muchos pasos de la Circle FFT y FRI, necesitamos gestionar dominios de evaluación de diferentes tamaños, todos los cuales se encuentran en la curva del círculo. Un método clave para lograr esto es el Lema 2 del documento de Circle STARKs, el cual asegura que un coset en posición estándar de tamaño puede dividirse en twin-cosets más pequeños de tamaño para cualquier .
7.1 Enunciado del Lema 2
Sea un coset en posición estándar de tamaño . Entonces, para cualquier , se puede descomponer en twin-cosets de tamaño . Concretamente, si
entonces
En términos más simples, si es un coset en posición estándar de tamaño , podemos cortarlo en twin-cosets más pequeños (cada uno de tamaño ) utilizando potencias de . Este particionamiento permite crear precisamente las partes de que se necesitan en un paso dado del protocolo Circle FFT.
7.2 Ejemplo Calculado a Mano: Dividiendo un coset en posición estándar de Tamaño en 2 Twin-Cosets de Tamaño
Supongamos que , de modo que . Tenemos subgrupos , donde .
- Nuestro coset en posición estándar
Sea con . Aquí, tiene orden , lo que significa que pero forma un coset de tamaño .
- Objetivo
Romper en twin-cosets de tamaño . Sea , así que y .
Entonces el Lema 2 sugiere
Dado que , tenemos y .
7.2.1 Caso
- .
- .
Calculamos:
Así, el primer twin-coset tiene 4 puntos distintos.
7.2.2 Caso
- .
Eso es . - Del mismo modo, .
De nuevo, multiplicamos cada uno por y :
Esto produce otros 4 puntos, formando el segundo twin-coset de tamaño 4.
7.2.3 Unión de los Dos Twin-Cosets
Tomar la unión de ambos conjuntos de 4 puntos (para y ) recupera todo el coset de 8 elementos . Por lo tanto:
Esto coincide exactamente con el enunciado del Lema 2, mostrando cómo un coset en posición estándar de 8 puntos puede ser descompuesto en dos twin-cosets, cada uno de tamaño 4.
7.3 Ejemplo Calculado a Mano: Dividiendo un coset en posición estándar de Tamaño en 4 Twin-Cosets de Tamaño
Y también podemos dividir este coset en posición estándar de tamaño 8 en múltiples twin-cosets más pequeños.
Sea , así que . El Lema 2 da:
Dado que , cada twin-coset tiene 2 puntos:
- :
- : $
={(24, 13), (24, 18)}$ - : ${Q^9 \cdot G_0 = Q^9, Q^{-9} \cdot G_0 = Q^{-9}}
$ - : $
={(7, 18), (7, 13)}$
Esto descompone en cuatro twin-cosets de tamaño .
8. El Mapeo de Cuadratura Reduce a la Mitad un Twin-Coset
Mientras que el Lema 2 se centra en dividir cosets grandes en twin-cosets más pequeños, el Lema 3 explota un mapeo de cuadratura para reducir a la mitad el tamaño de un twin-coset. Este proceso se utiliza para los pasos de reducción a la mitad del dominio en la Circle FFT, donde reducimos iterativamente el tamaño del dominio.
8.1 Enunciado del Lema 3
Este lema aparece en el documento de Circle STARKs, donde se utiliza para formalizar la reducción iterativa del dominio que es habilitada por el mapeo de cuadratura.
Supongamos que es un twin-coset de tamaño (con ). Entonces la aplicación del mapeo de cuadratura transforma a en otro twin-coset de tamaño . Además, si era un coset en posición estándar, entonces también sigue siendo un coset en posición estándar.
Intuitivamente, es un endomorfismo de grupo, y mapea un subgrupo hacia . Por lo tanto, un twin-coset de tamaño produce naturalmente otro twin-coset de tamaño .
8.1.1 Vista Abstracta de la Demostración del Lema 3
Sea un twin-coset de tamaño , que en notación abstracta significa
donde .
En particular, mapea el subgrupo hacia debido a un endomorfismo de grupo.
Esta es exactamente la definición de un twin-coset en el subgrupo , que tiene un tamaño de . En consecuencia, es en sí mismo un twin-coset de tamaño .
Si fue originalmente un coset en posición estándar, entonces la aplicación de preserva esa propiedad de posición estándar, porque . Así, es nuevamente un coset en posición estándar, pero ahora de tamaño .
8.2 Ejemplo Calculado a Mano (Reduciendo un Twin-Coset de Tamaño a Tamaño )
Asume y sea un twin-coset de tamaño . Concretamente,
Dado que , tenemos . Verificamos cómo afecta a :
8.2.1 Cálculo de y
8.2.2 Aplicación de a
El subgrupo tiene un tamaño de 2, digamos . Por el Lema 3,
$$
\begin{align*}
\pi(Q)\cdot(1,0) &= (27,27)\
\pi(Q)\cdot(30,0) &= (4,4)\
\pi(Q^{-1})\cdot(1,0) &= (27,4)\
\pi(Q^{-1})\cdot(30,0) &= (4,27)
\end{align*}
\cup
{(27,4), (4,27)}$$
el cual es un twin-coset de tamaño 4.
Por lo tanto, reduce a la mitad, pasando de 8 elementos a 4, exactamente como afirma el Lema 3. La aplicación repetida de continúa reduciendo el dominio en potencias de 2, desempeñando un papel crítico en la estructura recursiva de la Circle FFT.
En el diagrama a continuación:
- El círculo izquierdo muestra el twin-coset original , donde el rojo 🔴 = y el azul 🔵 = .
- El círculo derecho muestra el twin-coset reducido a la mitad , donde el azul 🔵 = y el rojo 🔴 = .
- Los puntos negros ⚫ marcan el subgrupo .
Este visual ilustra cómo mapea el dominio original de tamaño a un nuevo twin-coset de tamaño mediante el endomorfismo de grupo.

9. Lo Que Construimos en la Parte 1
En este artículo, nos centramos en cómo la curva del círculo sobre puede convertirse en un grupo cíclico de tamaño , y discutimos los twin-cosets y cosets en posición estándar con ejemplos concretos. También mostramos dos técnicas clave: una para dividir un coset en posición estándar más grande en twin-cosets más pequeños, y otra para reducir a la mitad el tamaño de un twin-coset aplicando el mapeo de cuadratura. Juntas, estas técnicas nos permiten remodelar dominios de tamaño a voluntad.
En la próxima parte, nos sumergiremos más a fondo en la propia Circle FFT, mostrando cómo estos dominios de tamaño —junto con la capacidad de dividirlos o reducirlos a la mitad—permiten una rápida evaluación e interpolación de polinomios, incluso cuando no es suficientemente 2-ádico.
Referencias
- Circle STARKs
https://eprint.iacr.org/2024/278 - Plonky3
https://github.com/Plonky3/Plonky3 - ZK11: Circle STARKs - Ulrich Haböck & Shahar Papini
https://youtu.be/NAhLYMysSdk?si=OlzNKS1DSTRkPnUR - Circle STARKs: Part I, Mersenne
https://blog.zksecurity.xyz/posts/circle-starks-1/ - Why I’m Excited By Circle Stark And Stwo
https://starkware.co/integrity-matters-blog/why-im-excited-by-circle-stark-and-stwo/ - Exploring circle STARKs
https://vitalik.eth.limo/general/2024/07/23/circlestarks.html - An introduction to circle STARKs
https://blog.lambdaclass.com/an-introduction-to-circle-starks/ - Yet another circle STARK tutorial
https://research.chainsafe.io/blog/circle-starks/ - Deep dive into Circle-STARKs FFT
https://ihagopian.com/posts/deep-dive-into-circle-starks-fft - Coset’s Circle STARKs lecture videos
https://youtube.com/playlist?list=PLbQFt1T_44DyihAOawprEABRPWgYeXB5u&si=AJe0dP7FUfp2Bebe - Ethereum/research/circlestark(Python implementation)
https://github.com/ethereum/research/tree/master/circlestark - Rust Implementation Demo of Circle Domain and Circle FFT
https://youtu.be/ur3c4mIi1Jc?si=lxU10mZ6vOFCrzvw
https://github.com/0xxEric/CircleFFT - STARKの原理
https://zenn.dev/qope/articles/8d60f77e3a7630
Apéndice
A continuación, introducimos la vista Proyectiva vs Afín de la curva del círculo, y luego explicamos por qué los Circle STARKs eligen específicamente los primos para evitar complicaciones proyectivas como los puntos en el infinito.
A.1 Proyectiva y Afín
En un plano afín sobre , un punto simplemente se escribe como con . Por el contrario, en el plano proyectivo , cada punto se denota por , pero todos los múltiplos escalares no nulos representan la misma ubicación. Formalmente,
- Cuando , podemos reescalar dividiendo cada coordenada entre , lo que nos da un punto afín .
- Cuando , obtenemos un punto en el infinito, que no tiene homólogo afín.
A.2 ¿Por qué Evitar los Puntos en el Infinito?
Para ver cómo surgen los puntos en el infinito, reescribe en forma proyectiva:
Aquí, si , consideramos e , de modo que se convierte en .
Un punto en el infinito es cualquier solución con . Que tales soluciones existan depende de si es un cuadrado en :
- Por ejemplo, si , entonces es un cuadrado en .
Concretamente, hay algún con , lo que implica que puede tener distintos de cero en .
Esto produce puntos infinitos adicionales en el círculo, complicando las operaciones de grupo y su implementación.
Ejemplo:- Aquí, en , y es en efecto .
- Entonces admite distintos de cero en , lo que da dos puntos en el infinito.
- La ecuación afín tiene exactamente cuatro soluciones afines:
- Y los puntos proyectivos en el infinito son
, hay soluciones en total — coincidiendo con ( afines + infinitos). - Si , entonces no es un cuadrado en .
En consecuencia, no tiene soluciones distintas de cero en . No obtenemos puntos en el infinito, y todas las soluciones permanecen siendo afines.
Ejemplo:- Dado que no es un cuadrado (tenemos y ), ningún satisface en .
- La ecuación tiene entonces solo soluciones afines. Comprobar produce:
para un total de soluciones, lo que coincide con .
En Circle STARK, elegimos específicamente para que no sea un cuadrado en . Esto asegura que nuestro círculo no tenga puntos infinitos (proyectivos), manteniendo cada solución en coordenadas afines y evita las complejidades de manejar puntos en el infinito.