From 6308f7142411683a8d422002d418625b14bf1b35 Mon Sep 17 00:00:00 2001 From: olegfomenko Date: Wed, 26 Feb 2025 14:02:00 +0200 Subject: [PATCH 1/9] Adding lecture about ECC --- lecture-notes-148x210.tex | 4 + lectures/14-ecc.tex | 260 +++++++++++++++++++++++++ lectures/images/lecture_14/circles.png | Bin 0 -> 94424 bytes 3 files changed, 264 insertions(+) create mode 100644 lectures/14-ecc.tex create mode 100644 lectures/images/lecture_14/circles.png diff --git a/lecture-notes-148x210.tex b/lecture-notes-148x210.tex index 9185e28..8328288 100644 --- a/lecture-notes-148x210.tex +++ b/lecture-notes-148x210.tex @@ -110,6 +110,10 @@ \section{Basics of STARKs} \subfile{lectures/13-stark} +\section{Introduction into the Error-Correcting codes} + +\subfile{lectures/14-ecc} + % --- Contact --- \newpage diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex new file mode 100644 index 0000000..69e8c69 --- /dev/null +++ b/lectures/14-ecc.tex @@ -0,0 +1,260 @@ +\documentclass[../lecture-notes.tex]{subfiles} + +\begin{document} + +\subsection{Introduction} +\textbf{Error-correcting codes (ECC)} are a group of protocols that wrap the input data extending it with an excessive +information that allows recovery of the original data even if the received message contains errors. Such methods are +widely used in the network protocols and data storage approaches. Moreover, they found many applications in the modern +cryptographic protocols (for example, Reed-Solomon ECC has been applied in the FRI protocol, which is widely used in +ZK-STARKs). Additionally, each code allows the determination of what errors exactly appeared in the received message +(sometimes, even if the message can not be decoded). + +ECC protocols can be divided in two groups: \textbf{block codes} and \textbf{convolutional codes}. The block codes +split information into the blocks of fixed pre-defined size and apply encoding to each block separately, while the +convolutional codes work over the input data stream of an arbitrary size. Modern practical block codes allow encoding +by polynomial complexity algorithms depending on the block size. Also, most of classic block codes leverage the +properties of the finite fields. + +In this article we primarily describe the structure of some classic block ECCs, including Walsh-Hadamard code and +Reed-Solomon code. Its aim is to give the reader an understanding of how they work and the mathematical primitives +they rely on. + +\subsection{Preliminaries} + +First of all let's define the main property of each ECC that directly determines its structure: +\begin{definition} +For the arbitrary binary strings of fixed length $x, y \in \{0, 1\}^n$ we define \textbf{Hamming distance} as the +number of elements where our binary strings do not match, divided by the length of the string: +$\Delta(x,y) = \frac{1}{n}|\{i: x_i \neq y_i\}|$. +\end{definition} +Then, we can define what exactly a block ECC is. Basically, it operates over some field ($GF(2)$ for example) encoding +information using some algorithm that differs for each code. +\begin{definition} +For each $\delta \in [0, 1]$ we call a function $E: \{0, 1\}^n \rightarrow \{0, 1\}^m$ an +\textbf{error-correcting code} with distance $\delta$ if for each two input strings $x \neq y \in \{0, 1\}^n$ the +distance between their images is more or equal $\delta$: $\Delta(E(x), E(y)) \geq \delta$. We call $Im(E)$ the set +of \textbf{codewords} of the corresponding code. +\end{definition} +Thus, each block ECC that takes an input word of size $n$ and outputs a codeword of size $m$ can theoretically deal +with $t = m-n$ errors. A block ECC also utilizes a relation between its distance and its theoretical correcting +capability: $t < \lfloor \frac{d-1}{2} \rfloor$. This relation exists because two arbitrary codewords can be decoded +if they contain at most $t$ errors. Therefore, the distance between these codewords must be at least $2t$ to enable +unique decoding (if it is less then two different codewords with errors can be represented as the same message, so the +pre-image cannot be found correctly). While the ECC distance is $d$, it leads to the relation above +(check Figure \ref{fig:distance}). +\begin{figure}[H] + \centering + \includegraphics[width=0.5\linewidth]{images/lecture_14/circles.png} + \caption{Relation between distance and the number of possible errors} + \label{fig:distance} +\end{figure} + +\subsection{Walsh-Hadamard code} +The Hadamard code, named after French mathematician Jacques Hadamard, is an error-correcting block code designed for +detecting and correcting errors in message transmissions over highly noisy or unreliable channels. In 1971, NASA +utilized this code to send images of Mars from the Mariner 9 space probe back to Earth. This code is also referred to +as the Walsh code, Walsh family, or Walsh–Hadamard code, in honor of American mathematician Joseph Leonard Walsh. + +\begin{definition}[Walsh-Hadamard code] +For the binary strings $x, y \in \{0, 1\}^n$ let's define $\langle x, y \rangle = \sum_{i = 0}^{n-1} x_iy_i\mod 2$. +The Walsh-Hadamard code is a function $E: \{0, 1\}^n \rightarrow \{0, 1\}^{2^n}$ that maps each input of size $n$ into +the string $z \in \{0, 1\}^{2^n}$ where $z_y = \langle x, y \rangle$ for $\forall y \in \{0, 1\}^n$. +\end{definition} + +\begin{example} +\textbf{Encoding.} For example, for the input size $n = 2$ we have the following set of the possible codewords: +\begin{equation*} + \{(0, 0), (0, 1), (1, 0), (1, 1)\} +\end{equation*} +Then, for each input codeword the Walsh-Hadamard code will be: +\begin{equation*} + E(x) = \{\langle x, (0, 0) \rangle, \langle x, (0, 1) \rangle, \langle x, (1, 0) \rangle, \langle x, (1, 1) \rangle\} +\end{equation*} +For example, taking $x = (0, 1)$ we have: +\begin{gather*} + E(\{0, 1\}) = \{\\ + \langle (0, 1), (0, 0) \rangle,\\ + \langle (0, 1), (0, 1) \rangle,\\ + \langle (0, 1), (1, 0) \rangle,\\ + \langle (0, 1), (1, 1) \rangle\\ + \}=(1, 0, 0, 1) +\end{gather*} +\textbf{Decoding.} To decode a received codeword, we use the following technique: for each possible input word +$x \in \{0, 1\}^n$ we calculate the list of $C_x(y) = (-1)^{\langle x, y \rangle}$ for each $y \in \{0, 1\}^n$. We +also represent our received codeword $Y$ as list of $(-1)^{Y_i}$. For each possible word $u \in \{0, 1\}^n$ we +calculate $S(u) = \sum_{i=0}^{2^n - 1} (-1)^{Y_i} \cdot C_u(i)$. This sum represents the similarity between the word +$u$ and the encoded word. If both words have the same sign at position $i$, the sum increases; otherwise, it +decreases. The decoded word is the one with the highest sum. +\end{example} +\begin{lemma}[Random subsum principle] +For two binary strings $u \neq v \in \{0, 1\}^n$ the $\Pr[\langle u, x \rangle \neq \langle v, x \rangle] = \frac{1}{2}$ + for the random binary string $x \in \{0, 1\}^n$. +\end{lemma} +\begin{proof} +Note, that $\langle u, x \rangle \neq \langle v, x \rangle$ works if and only if +$1 = \langle u, x \rangle + \langle v, x \rangle = \langle (u + v), x \rangle$, where $+$ is an addition by modulo +$2$ (aka \verb|XOR|). Then, we can rewrite this as $\langle (u + v), x \rangle = \sum_{(u + v)_i \neq 0} x_i \mod 2$. +While $u + v \neq 0^n$ from the initial assumption and $x$ is a uniformly random string, the +$\sum_{(u + v)_i \neq 0} x_i \mod 2 = 1$ with probability $\frac{1}{2}$. So, finally, +$\langle u, x \rangle \neq \langle v, x \rangle$ with probability $\frac{1}{2}$. +\end{proof} +\begin{lemma} +The Walsh-Hadamard code is an error-correcting code with distance $\frac{1}{2}$. +\end{lemma} +\begin{proof} +Taking two codewords $f(x_1)$ and $f(x_2)$ the distance between them is equal to the number of $y \in \{0, 1\}^n$ where +$\langle x_1, y \rangle \neq \langle x_1, y \rangle$. We know that this happens in the half of the cases, so for a +codeword of size $2^n$ the distance will be $\Delta(f(x_1), f(x_2)) = \frac{2^{n-1}}{2^n} = \frac{1}{2}$. +\end{proof} +This code made a significant impact on the coding theory, mathematics, and theoretical computer science. However, it is +impractical for modern applications due to the exponential growth in the size of the codewords. + + +\subsection{Reed-Solomon code} +In information and coding theory, Reed–Solomon code is a block error-correcting code +developed by Irving S. Reed and Gustave Solomon in 1960. It (and it's variations) is widely used across various +applications, including consumer technologies like MiniDiscs, CDs, DVDs, Blu-ray discs, QR codes, and Data Matrix. It +also play a crucial role in data transmission systems such as DSL and WiMAX, broadcast technologies like satellite +communications, DVB, and ATSC, as well as storage solutions such as RAID6. + +Firstly, let's start from the definition of the input data that differs from binary: +\begin{definition} +For some alphabet $\Sigma$ elements $x,y \in \Sigma^n$ we define $\Delta(x, y) = \frac{1}{n}|\{i: x_i \neq y_i\}|$. +\end{definition} +Now we can describe error-correcting codes for the alphabets that differ from binary. Also, let's describe one +subclass of the block ECCs to which the Reed-Solomon code belong: +\begin{definition}[Linear code] +Linear code is a code where the set of codewords forms a linear space: +\begin{itemize} + \item[--] For each two codewords $c_1, c_2$, the $c_1 + c_2$ is also a codeword. + \item[--] For the codeword $c$ and constant $\alpha$ the $\alpha c$ is also a codeword. +\end{itemize} +\end{definition} +\begin{theorem} +Let the \textbf{weight} of a codeword be the number of positions where its value is non-zero. Then, the distance of +the linear error-correcting code is equal to the minimum possible weight of non-zero codeword. +\end{theorem} +\begin{proof} +For two codewords $c_1, c_2$, the $c_1 - c_2$ is also a codeword. Note, that $\Delta(c_1, c_2) = w(c_1-c_2)$, +where $w(x) = |\{i: x_i \neq 0\}|$ is a weight function and subtraction is done by modulo 2 over binary elements. +Thus, the distance of the linear error-correcting code will be equal to the minimum possible distance between two +non-equal codewords that is also equal to the minimum possible weight of the non-zero codeword: +\begin{equation*} + \delta = \min_{c_1 \neq c_2} \Delta(c_1, c_2) = \min_{c_1 \neq c_2} w(c_1 - c_2) = \min_{c \neq 0} w(c) +\end{equation*} +\end{proof} +Note, that the Walsh-Hadamard code is also a linear code, so we can prove its distance using this approach as well. + + +\begin{definition}[Reed-Solomon code] +For a field $\mathbb{F}$ and numbers $0 < n \leq m < |\mathbb{F}|$ the \textbf{Reed-Solomon code} from +$\mathbb{F}^n \rightarrow \mathbb{F}^m$ is a function that takes $n-1$ degree polynomial $A(x) \in \mathbb{F}[x]$ +and outputs its evaluation over $m$ points $f_0,...,f_{m-1} \in \mathbb{F}$. +\end{definition} + +\begin{lemma} +The Reed-Solomon code has distance of $1 - \frac{n-1}{m}$ +\end{lemma} +\begin{proof} +The word is a polynomial of degree less then $n$ so it can have no more then $n-1$ roots. The codeword is the +evaluation of this polynomial at $m > n$ distinct points, meaning it must contain at least $m-(n-1)$ non-zero +values (since at most $n-1$ points in $f$ can be roots of our word polynomial). Since the Reed-Solomon code is a +linear code, we can use its property to define its distance as $\delta =\frac{m - n + 1}{n} = 1 - \frac{n-1}{m}$. +\end{proof} + +By following the ``Unisolvence theorem'' we know that to recover $n-1$ degree polynomial we need at least $n$ points +for interpolation. Because the Reed-Solomon code performs polynomial evaluation over $m > n$ points we can still +interpolate (or decode) the polynomial even if some points are corrupted during data transmission. + + +\begin{theorem} +There exists a polynomial-time (depending on the codeword size) algorithm with the following properties: +\begin{itemize} + \item[--] \verb|Input|: a list of pairs $(a_i, b_i)|^{m-1}_0$ of elements in $\mathbb{F}$ such that for + $\hat{t} > \frac{m}{2} + \frac{n}{2}$ of them $G(a_i) = b_i$ for some unique polynomial $G(x)$ with $\deg G(x) = n-1$. + \item[--] \verb|Output|: a polynomial $G(x)$ with $\deg G(x) = n-1$. +\end{itemize} +\end{theorem} + +\begin{proof} +The assumption $\hat{t} > \frac{m}{2} + \frac{n}{2}$ can be transformed into the corresponding constraint on the number +of possible errors $t$: +\begin{equation*} + t = m - \hat{t} < m - \frac{m}{2} - \frac{n}{2} = \frac{m}{2} - \frac{n}{2} +\end{equation*} +Let's also evaluate a necessary constraint that will be useful in the algorithm below: +\begin{gather*} + t < \frac{m}{2} - \frac{n}{2}\\ + 2t < m - n\\ + m > 2t+n +\end{gather*} +So, we know the lower bound of the codeword size depending on the word and number of errors: $m \geq 2t+n+1$. + +\begin{tcolorbox}[title=Berlekamp-Welch decoding, + breakable, + colback=blue!5!white, + colframe=blue!75!black, + colbacktitle=blue!25!white, + coltitle=blue!20!black, + fonttitle=\bfseries, + boxrule=1.25pt, + subtitle style={boxrule=0pt, + colback=blue!20!white, + colupper=blue!75!gray} ] + + So, $G(a_i) = b_i$ for at least $\hat{t}$ of $m$ pairs. We also know that $\deg G(x) = n-1$ and $n < t < m$. + It means that we already can recover polynomial but we firstly have to deal with an errors. + + \tcbsubtitle{$\mathsf{Decoding}(\mathbb{F}, n, m, (a_i, b_i)|^{m-1}_0)$} + + \begin{itemize}[label=\ding{51}] + \item Let's put an \textbf{error polynomial} $E(X)$ a polynomial which has roots at the error points. + \item So, $\deg E(x) = t < \frac{m}{2} - \frac{n}{2}$. + \item Our algorithm is based on the following equation: $$C(a_i) = G(a_i)\cdot E(a_i), i \in 0..m-1$$ + where $C(x)$ is an arbitrary polynomial that we are going to find. + \item Note, that $\deg C(x) = \deg G(x) + \deg E(x) = n - 1 + t$ + \item Then, by solving the equation $C(a_i) = b_i\cdot E(a_i), i \in 0..m-1$ we can find $C(x)$ and $E(x)$. + \item This can be considered as a set of $m$ linear equations where we have unknown $n+t$ coefficients + of $C(x)$ and $t+1$ coefficient of $E(x)$, so it can be solved via linear algebra while $m \geq 2t+n+1$. + \item Finally, we put $G(x) = \frac{C(x)}{E(x)}$ + \end{itemize} + +\end{tcolorbox} +The last equation follows from the observation that polynomial $C(x) - G(x)\cdot E(x)$ is zero in $\hat{t}$ points +while it has degree $n + t - 1$. It is easy to prove that the polynomial degree less then the number of it's roots, +so $C(x) - G(x)\cdot E(x)$ is zero for each $x$. That is why from $C(x) = E(x)G(x)$ follows that $C(x)$ is divisible +on $E(x)$. +\end{proof} + +\subsection{Convolutional codes} +A convolutional code is an ECC that utilizes the following properties: +\begin{enumerate} +\item[--] For each $k$ input bits it generates $n>k$ output bits; +\item[--] The transformation also depends on the $m$ previous bits; +\item[--] The ECC function is linear: for two inputs $x, y$, corresponding ECC's outputs $X, Y$ and scalars $a,b$ the +following holds: +\begin{equation*} + ax + by = aX + bY +\end{equation*} +\end{enumerate} +For example, the \textbf{linear-feedback shift register} (LFSR) ECC for each $k$ input bits provides $n$ output bits +while remembering $m$ previous bits to use during the encoding process. +\begin{definition}[Convolutional rate] + We call a \textbf{code rate} as the ratio between the input and output sizes: +\begin{equation*} + R = \frac{k}{n} +\end{equation*} +\end{definition} +For example a rate $\frac{1}{3}$ means that for each input bit the code will generate $3$ output bits. Usually, LFSR +operates with rates $\frac{1}{2}$ or $\frac{1}{3}$. Each LFSR code defines the output function per each output bit that +defines how it will be calculated. Usually, this function uses \verb|XOR| over the pre-defined positions of the +previously read values to generate the output (initially they are all zeros). So, during each iteration of the LFSR, +it reads the next bit (or $k$ bits) of the input (which is equivalent to shifting the whole sequence to the right, +which allows to add incoming bits from the left), updates the state of the already read bits (utilizing the +first-in-first-out over the queue of size $m$) and calculates $n$ output bits. + +To decode the output, convolutional codes utilize different algorithms (for example the Sequential Decoding or Viterbi +algorithm), including, but not limited to, heuristic and probabilistic algorithms. All of them differ in computational +complexity and correctness of the result. +\end{document} \ No newline at end of file diff --git a/lectures/images/lecture_14/circles.png b/lectures/images/lecture_14/circles.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f3e39415bb6e1d318fb52cc2a4b5bfe0caf0c5 GIT binary patch literal 94424 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 Date: Wed, 26 Feb 2025 14:14:30 +0200 Subject: [PATCH 2/9] small fixes --- lectures/14-ecc.tex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index 69e8c69..6232f7a 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -62,8 +62,8 @@ \subsection{Walsh-Hadamard code} the string $z \in \{0, 1\}^{2^n}$ where $z_y = \langle x, y \rangle$ for $\forall y \in \{0, 1\}^n$. \end{definition} -\begin{example} -\textbf{Encoding.} For example, for the input size $n = 2$ we have the following set of the possible codewords: +\begin{example}[Encoding and Decoding] +For example, for the input size $n = 2$ we have the following set of the possible codewords: \begin{equation*} \{(0, 0), (0, 1), (1, 0), (1, 1)\} \end{equation*} @@ -87,6 +87,7 @@ \subsection{Walsh-Hadamard code} $u$ and the encoded word. If both words have the same sign at position $i$, the sum increases; otherwise, it decreases. The decoded word is the one with the highest sum. \end{example} +To define the distance for the Walsh-Hadamard ECC we first need to prove an additional lemma: \begin{lemma}[Random subsum principle] For two binary strings $u \neq v \in \{0, 1\}^n$ the $\Pr[\langle u, x \rangle \neq \langle v, x \rangle] = \frac{1}{2}$ for the random binary string $x \in \{0, 1\}^n$. From c1177268e2befdd6174bcbdc1dd79619d6690259 Mon Sep 17 00:00:00 2001 From: olegfomenko Date: Mon, 10 Mar 2025 23:03:57 +0200 Subject: [PATCH 3/9] adding implementation of RS --- lectures/14-ecc.tex | 90 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index 6232f7a..13db216 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -24,11 +24,14 @@ \subsection{Preliminaries} First of all let's define the main property of each ECC that directly determines its structure: \begin{definition} -For the arbitrary binary strings of fixed length $x, y \in \{0, 1\}^n$ we define \textbf{Hamming distance} as the -number of elements where our binary strings do not match, divided by the length of the string: +For the arbitrary binary strings of fixed length $x, y \in \{0, 1\}^n$ we define an \textbf{absolute Hamming distance} +as the number of elements where our binary strings do not match: $\Delta(x,y) = |\{i: x_i \neq y_i\}|$. We also define +the \textbf{normalized Hamming distance} as the absolute Hamming distance divided by the length of the string: $\Delta(x,y) = \frac{1}{n}|\{i: x_i \neq y_i\}|$. \end{definition} -Then, we can define what exactly a block ECC is. Basically, it operates over some field ($GF(2)$ for example) encoding +In the theorems below we will use the normalized Hamming distance because of the independence from codeword length. + +Next, we can define what exactly a block ECC is. Basically, it operates over some field ($\mathbb{GF}(2)$ for example) encoding information using some algorithm that differs for each code. \begin{definition} For each $\delta \in [0, 1]$ we call a function $E: \{0, 1\}^n \rightarrow \{0, 1\}^m$ an @@ -113,11 +116,12 @@ \subsection{Walsh-Hadamard code} \subsection{Reed-Solomon code} -In information and coding theory, Reed–Solomon code is a block error-correcting code -developed by Irving S. Reed and Gustave Solomon in 1960. It (and it's variations) is widely used across various -applications, including consumer technologies like MiniDiscs, CDs, DVDs, Blu-ray discs, QR codes, and Data Matrix. It +In information and coding theory, Reed–Solomon ECCs are a group of block error-correcting codes +developed by Irving S. Reed and Gustave Solomon in 1960. They are widely used across various +applications, including consumer technologies like MiniDiscs, CDs, DVDs, Blu-ray discs, QR codes, and Data Matrix. They also play a crucial role in data transmission systems such as DSL and WiMAX, broadcast technologies like satellite -communications, DVB, and ATSC, as well as storage solutions such as RAID6. +communications, DVB, and ATSC, as well as storage solutions such as RAID6. Essentially, the difference between the variants +Reed-Solomon code lies in the assumptions on the basis of which their encoding and decoding algorithms are determined. Firstly, let's start from the definition of the input data that differs from binary: \begin{definition} @@ -228,6 +232,78 @@ \subsection{Reed-Solomon code} on $E(x)$. \end{proof} +\subsubsection{Reed-Solomon(255,223) implementation} +The example implementation aims to help reader to understand how the encoding and decoding looks like. The +Reed-Solomon code with $m=255, n=223$ is widely used in communication and storage systems, including QR codes, CDs, +DVDs, and deep-space communication. It operates over $\mathbb{GF}(256)$ (the finite field of $256$ elements) +and encodes data as polynomials evaluated at different field elements. Following our theorem, the number of errors +is constrained as $t < \frac{m}{2} - \frac{n}{2}$ which means that $t < 16$. For our implementation we take $t = 15$. + +Let's start from the basic parameters definition: +\begin{lstlisting}[language=Python,numbers=none] +F = GF(256) +R. = PolynomialRing(F) # define x as a GF(256) element +m = 255 # A codeword size +n = 223 # A word size +t = 15 # The number of possible errors +distance = (m - n + 1)/m +\end{lstlisting} +The \verb|distance| then is equal to $\frac{11}{85}$. Let's sample a random message that will be an information +polynomial of degree $n=223$ (this messsage can be obtained by the interpolation of some useful data): +\begin{lstlisting}[language=Python,numbers=none] +word = sum(F.random_element() * x^i for i in range(n)) +\end{lstlisting} +We start encoding from defining the evaluation elements that will be all non-zero elements of $\mathbb{GF}(256)$. +Then, our codeword will be the $m = 255$ evaluations of our information polynomial. +\begin{lstlisting}[language=Python,numbers=none] +# All non zero elements in F. Order(F) - 1 = 255 +f = [f_i for f_i in F if f_i != 0] +assert len(f) == m +codeword = [word(f_i) for f_i in f] +assert len(codeword) == m +\end{lstlisting} +To decode the codeword we should solve the system of $m$ equations $C(x_i) - b_i \cdot E(x_i) = 0$. Note, that this system +is homogeneous, so the trivial solution (all zeros) is always a solution. To find a non-zero solution we impose a +normalization condition, such as setting the leading coefficient of $E(x)$ to $1$. This turns our original equation +into the $C(x_i) - b_i \cdot E(x_i) = b_i \cdot x_i^deg_E$. +\begin{lstlisting}[language=Python,numbers=none] +deg_C = 237 # deg = n - 1 + t +deg_E = 15 # deg = t + +M = Matrix(F, m, deg_C + deg_E + 1) +for i, (f_i, b_i) in enumerate(zip(f, codeword)): + # Fill in coefficients for C(x) + for j in range(deg_C + 1): + M[i, j] = f_i^j + # Fill in coefficients for E(x), multiplied by -b_i + for j in range(deg_E): + M[i, deg_C + 1 + j] = -b_i * (f_i^j) + + +rhs = vector(F, [b_i * (f_i^deg_E) for f_i, b_i in zip(f, codeword)]) +# Solve the linear system over F +solution = M.solve_right(rhs) +assert len(solution) == deg_C + deg_E + 1 +\end{lstlisting} +Finally, we can recover the original information polynomial $G(x) = \frac{C(x)}{E(x)}$. We also should not forget to +add the normalized leading coefficient of $E(x)$. +\begin{lstlisting}[language=Python,numbers=none] +C = sum(solution[i] * x^i for i in range(deg_C + 1)) +E = sum(solution[deg_C + 1 + i] * x^i for i in range(deg_E)) +E += x ^ deg_E +G = C/E +assert G.numerator() == word +\end{lstlisting} + +To discrover the error correcting properties of the presented code you can add the following snippet after the +codeword calculation. It changes first $t$ elements of codeword to the random $\mathbb{GF}(256)$ elements. If you try +to change more elements then the linear system solving procedure will fail. +\begin{lstlisting}[language=Python,numbers=none] +for i in range(t): + codeword[i] = F.random_element() +\end{lstlisting} + + \subsection{Convolutional codes} A convolutional code is an ECC that utilizes the following properties: \begin{enumerate} From 8d18478f5c1e63384b914b98a102cc2fbf095849 Mon Sep 17 00:00:00 2001 From: ZamDimon Date: Tue, 11 Mar 2025 03:25:53 +0200 Subject: [PATCH 4/9] :adhesive_bandage: refactored Reed-Solomon codes section --- lectures/14-ecc.tex | 412 +++++++++++++++++++++++------------ preface/acknowledgements.pdf | Bin 0 -> 25533 bytes preface/preface.pdf | Bin 0 -> 42345 bytes zkdl-template.cls | 2 +- 4 files changed, 272 insertions(+), 142 deletions(-) create mode 100644 preface/acknowledgements.pdf create mode 100644 preface/preface.pdf diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index 13db216..ecb597a 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -1,51 +1,82 @@ -\documentclass[../lecture-notes.tex]{subfiles} +\documentclass[../lecture-notes-148x210.tex]{subfiles} \begin{document} \subsection{Introduction} -\textbf{Error-correcting codes (ECC)} are a group of protocols that wrap the input data extending it with an excessive -information that allows recovery of the original data even if the received message contains errors. Such methods are -widely used in the network protocols and data storage approaches. Moreover, they found many applications in the modern -cryptographic protocols (for example, Reed-Solomon ECC has been applied in the FRI protocol, which is widely used in -ZK-STARKs). Additionally, each code allows the determination of what errors exactly appeared in the received message -(sometimes, even if the message can not be decoded). - -ECC protocols can be divided in two groups: \textbf{block codes} and \textbf{convolutional codes}. The block codes -split information into the blocks of fixed pre-defined size and apply encoding to each block separately, while the -convolutional codes work over the input data stream of an arbitrary size. Modern practical block codes allow encoding -by polynomial complexity algorithms depending on the block size. Also, most of classic block codes leverage the +\textbf{Error-correcting codes (ECC)} are a group of protocols that wrap the +input data extending it with an excessive information that allows recovery of +the original data even if the received message contains errors. Such methods are +widely used in the network protocols and data storage approaches. Moreover, they +found many applications in the modern cryptographic protocols (for example, +Reed-Solomon ECC has been applied in the FRI protocol, which is widely used in +ZK-STARKs). Additionally, each code allows the determination of what errors +exactly appeared in the received message (sometimes, even if the message can not +be decoded). + +ECC protocols can be divided in two groups: \textbf{block codes} and +\textbf{convolutional codes}. The block codes split information into the blocks +of fixed pre-defined size and apply encoding to each block separately, while the +convolutional codes work over the input data stream of an arbitrary size. Modern +practical block codes allow encoding by polynomial complexity algorithms +depending on the block size. Also, most of classic block codes leverage the properties of the finite fields. -In this article we primarily describe the structure of some classic block ECCs, including Walsh-Hadamard code and -Reed-Solomon code. Its aim is to give the reader an understanding of how they work and the mathematical primitives -they rely on. +In this section we primarily describe the structure of some classic block ECCs, +including Walsh-Hadamard code and Reed-Solomon code. Its aim is to give the +reader an understanding of how they work and the mathematical primitives they +rely on. \subsection{Preliminaries} First of all let's define the main property of each ECC that directly determines its structure: \begin{definition} -For the arbitrary binary strings of fixed length $x, y \in \{0, 1\}^n$ we define an \textbf{absolute Hamming distance} -as the number of elements where our binary strings do not match: $\Delta(x,y) = |\{i: x_i \neq y_i\}|$. We also define -the \textbf{normalized Hamming distance} as the absolute Hamming distance divided by the length of the string: -$\Delta(x,y) = \frac{1}{n}|\{i: x_i \neq y_i\}|$. +For the arbitrary binary strings of fixed length $x, y \in \{0, 1\}^n$ we define +an \textbf{absolute Hamming distance} as the number of elements where our binary +strings do not match: $\Delta_H(x,y) = \left|\{i \in [n]: x_i \neq y_i\}\right|$. We also +define the \textbf{normalized Hamming distance} as the absolute Hamming distance +divided by the length of the string: $\Delta(x,y) = \frac{1}{n}\left|\{i \in [n]: x_i +\neq y_i\}\right|$. \end{definition} -In the theorems below we will use the normalized Hamming distance because of the independence from codeword length. -Next, we can define what exactly a block ECC is. Basically, it operates over some field ($\mathbb{GF}(2)$ for example) encoding -information using some algorithm that differs for each code. +In the theorems below we will use the normalized Hamming distance +$\Delta(\cdot,\cdot)$ because of the independence from codeword length. + +Next, we can define what exactly a block ECC is. In its essence, this is a +function, operating over some field (over binary field $\mathbb{F}_2$, for +example), encoding information using some algorithm $E$ that differs for each +code. Typically, the input to this encoder function is called a \textbf{word} or +\textbf{message}, while the image of such function $\mathsf{im}(E)$ is what we +call a \textbf{codeword}. Let us define that more formally. \begin{definition} -For each $\delta \in [0, 1]$ we call a function $E: \{0, 1\}^n \rightarrow \{0, 1\}^m$ an -\textbf{error-correcting code} with distance $\delta$ if for each two input strings $x \neq y \in \{0, 1\}^n$ the -distance between their images is more or equal $\delta$: $\Delta(E(x), E(y)) \geq \delta$. We call $Im(E)$ the set -of \textbf{codewords} of the corresponding code. +For each $\delta \in [0, 1]$ we call a function $E: \{0, 1\}^n \rightarrow \{0, +1\}^m$ an \textbf{error-correcting code} with distance $\delta$ if for each two +\emph{different} input strings $x \neq y \in \{0, 1\}^n$ the distance between +their images is more or equal $\delta$: that is, $\Delta(E(x), E(y)) \geq +\delta$. We call the image of the function $E$ +\begin{equation*} + \mathsf{im}(E) \equiv \{y \in \{0,1\}^m \; \text{such that} \; y=E(x) \; \text{for some} \; x \in \{0,1\}^n \} +\end{equation*} +the set of \textbf{codewords} of the corresponding code. \end{definition} -Thus, each block ECC that takes an input word of size $n$ and outputs a codeword of size $m$ can theoretically deal -with $t = m-n$ errors. A block ECC also utilizes a relation between its distance and its theoretical correcting -capability: $t < \lfloor \frac{d-1}{2} \rfloor$. This relation exists because two arbitrary codewords can be decoded -if they contain at most $t$ errors. Therefore, the distance between these codewords must be at least $2t$ to enable -unique decoding (if it is less then two different codewords with errors can be represented as the same message, so the -pre-image cannot be found correctly). While the ECC distance is $d$, it leads to the relation above -(check Figure \ref{fig:distance}). + +\begin{remark} + In fact, the distance $\delta$ in the definition above is a lower bound of the + distance between the codewords. This can be formally written as: + \begin{equation*} + \delta = \min_{x \neq y \in \mathsf{im}(E)} \Delta(x, y) + \end{equation*} +\end{remark} + +Therefore, each block ECC that takes an input word of size $n$ and outputs a +codeword of size $m>n$ can theoretically deal with $t=m-n$ errors. A block ECC +also utilizes a relation between its distance and its theoretical correcting +capability: $t < \lfloor \frac{d-1}{2} \rfloor$. This relation exists because +two arbitrary codewords can be decoded if they contain at most $t$ errors. +Therefore, the distance between these codewords must be at least $2t$ to enable +unique decoding (if it is less then two different codewords with errors can be +represented as the same message, so the pre-image cannot be found correctly). +While the ECC distance is $d$, it leads to the relation above (check Figure +\ref{fig:distance}). \begin{figure}[H] \centering \includegraphics[width=0.5\linewidth]{images/lecture_14/circles.png} @@ -54,67 +85,104 @@ \subsection{Preliminaries} \end{figure} \subsection{Walsh-Hadamard code} -The Hadamard code, named after French mathematician Jacques Hadamard, is an error-correcting block code designed for -detecting and correcting errors in message transmissions over highly noisy or unreliable channels. In 1971, NASA -utilized this code to send images of Mars from the Mariner 9 space probe back to Earth. This code is also referred to -as the Walsh code, Walsh family, or Walsh–Hadamard code, in honor of American mathematician Joseph Leonard Walsh. +The Hadamard code, named after French mathematician Jacques Hadamard, is an +error-correcting block code designed for detecting and correcting errors in +message transmissions over highly noisy or unreliable channels. In 1971, NASA +utilized this code to send images of Mars from the Mariner 9 space probe back to +Earth. This code is also referred to as the Walsh code, Walsh family, or +Walsh–Hadamard code, in honor of American mathematician Joseph Leonard Walsh. -\begin{definition}[Walsh-Hadamard code] -For the binary strings $x, y \in \{0, 1\}^n$ let's define $\langle x, y \rangle = \sum_{i = 0}^{n-1} x_iy_i\mod 2$. -The Walsh-Hadamard code is a function $E: \{0, 1\}^n \rightarrow \{0, 1\}^{2^n}$ that maps each input of size $n$ into -the string $z \in \{0, 1\}^{2^n}$ where $z_y = \langle x, y \rangle$ for $\forall y \in \{0, 1\}^n$. +But first, let us define the inner product over binary string. + +\begin{definition}[Inner product] +For two binary strings $x, y \in \{0, 1\}^n$ the \textbf{inner product} $\langle +\cdot, \cdot \rangle: \{0,1\}^n \times \{0,1\}^n \to \mathbb{F}_2$ is defined +as: +\begin{equation*} + \langle x, y \rangle = \sum_{i \in [n]} x_iy_i \; \text{over} \; \mathbb{F}_2 +\end{equation*} \end{definition} -\begin{example}[Encoding and Decoding] -For example, for the input size $n = 2$ we have the following set of the possible codewords: +Now, we are ready to define the Walsh-Hadamard code. + +\begin{definition}[Walsh-Hadamard code] +The \textbf{Walsh-Hadamard code} is a function $\mathsf{Had}: \{0, 1\}^n +\rightarrow \{0, 1\}^{2^n}$ that maps each $x \in \{0,1\}^n$ into the string $z +\in \{0, 1\}^{2^n}$ of size $2^n$ where $z_y = \langle x, y\rangle$ for every $y +\in \{0, 1\}^n$. More concisely, this is written as: \begin{equation*} - \{(0, 0), (0, 1), (1, 0), (1, 1)\} + \mathsf{Had}(x) = \left(\langle x, y \rangle\right)_{y \in \{0, 1\}^n} \end{equation*} -Then, for each input codeword the Walsh-Hadamard code will be: +\end{definition} + +\begin{example}[Encoding and Decoding] +For example, for the input size $n=2$, the message space consists of $4$ +elements: $\{0,1\}^2 = \{(0, 0), (0, 1), (1, 0), (1, 1)\}$. Then, for each input codeword +the Walsh-Hadamard code will be: \begin{equation*} - E(x) = \{\langle x, (0, 0) \rangle, \langle x, (0, 1) \rangle, \langle x, (1, 0) \rangle, \langle x, (1, 1) \rangle\} + \mathsf{Had}(x) = (\langle x, y \rangle)_{y \in \{0,1\}^2} = (\langle x, (0, 0) \rangle, \langle x, (0, 1) \rangle, \langle x, (1, 0) \rangle, \langle x, (1, 1) \rangle) \end{equation*} For example, taking $x = (0, 1)$ we have: \begin{gather*} - E(\{0, 1\}) = \{\\ - \langle (0, 1), (0, 0) \rangle,\\ - \langle (0, 1), (0, 1) \rangle,\\ - \langle (0, 1), (1, 0) \rangle,\\ + \mathsf{Had}(\{0, 1\}) = \begin{bmatrix} + \langle (0, 1), (0, 0) \rangle\\ + \langle (0, 1), (0, 1) \rangle\\ + \langle (0, 1), (1, 0) \rangle\\ \langle (0, 1), (1, 1) \rangle\\ - \}=(1, 0, 0, 1) + \end{bmatrix}= \begin{bmatrix} + 0 \cdot 0 + 1 \cdot 0 \\ + 0 \cdot 0 + 1 \cdot 1 \\ + 0 \cdot 1 + 1 \cdot 0 \\ + 0 \cdot 1 + 1 \cdot 1 \\ + \end{bmatrix} = (0, 1, 0, 1) \end{gather*} -\textbf{Decoding.} To decode a received codeword, we use the following technique: for each possible input word -$x \in \{0, 1\}^n$ we calculate the list of $C_x(y) = (-1)^{\langle x, y \rangle}$ for each $y \in \{0, 1\}^n$. We -also represent our received codeword $Y$ as list of $(-1)^{Y_i}$. For each possible word $u \in \{0, 1\}^n$ we -calculate $S(u) = \sum_{i=0}^{2^n - 1} (-1)^{Y_i} \cdot C_u(i)$. This sum represents the similarity between the word -$u$ and the encoded word. If both words have the same sign at position $i$, the sum increases; otherwise, it -decreases. The decoded word is the one with the highest sum. +\textbf{Decoding.} To decode a received codeword, we use the following +technique: for each possible input word $x \in \{0, 1\}^n$, we calculate $C(x,y) += (-1)^{\langle x, y \rangle}$ for each $y \in \{0, 1\}^n$. We also represent +our received codeword $\widetilde{y}$ as $\{(-1)^{\widetilde{y}_i}\}_{i \in +[2^n]}$. For each possible word $\widetilde{x} \in \{0, 1\}^n$ we calculate the +sum $S(u) = \sum_{s \in [2^n]} (-1)^{\widetilde{y}_i} \cdot C(\widetilde{x},s)$. +This sum represents the similarity between the word $\widetilde{x}$ and the +encoded word. If both words have the same sign at position $s$, the sum +increases; otherwise, it decreases. The decoded word is the one with the highest +sum. \end{example} To define the distance for the Walsh-Hadamard ECC we first need to prove an additional lemma: \begin{lemma}[Random subsum principle] -For two binary strings $u \neq v \in \{0, 1\}^n$ the $\Pr[\langle u, x \rangle \neq \langle v, x \rangle] = \frac{1}{2}$ - for the random binary string $x \in \{0, 1\}^n$. +The following statement holds: +\begin{equation*} + \Pr\left[\langle u, x \rangle \neq \langle v, x \rangle \;\middle| \; + \begin{matrix} + u \neq v \in \{0,1\}^n, \\ + x \xleftarrow{R} \{0,1\}^n + \end{matrix} \right] = \frac{1}{2} +\end{equation*} \end{lemma} + \begin{proof} -Note, that $\langle u, x \rangle \neq \langle v, x \rangle$ works if and only if -$1 = \langle u, x \rangle + \langle v, x \rangle = \langle (u + v), x \rangle$, where $+$ is an addition by modulo -$2$ (aka \verb|XOR|). Then, we can rewrite this as $\langle (u + v), x \rangle = \sum_{(u + v)_i \neq 0} x_i \mod 2$. -While $u + v \neq 0^n$ from the initial assumption and $x$ is a uniformly random string, the -$\sum_{(u + v)_i \neq 0} x_i \mod 2 = 1$ with probability $\frac{1}{2}$. So, finally, -$\langle u, x \rangle \neq \langle v, x \rangle$ with probability $\frac{1}{2}$. +Note, that $\langle u, x \rangle \neq \langle v, x \rangle$ holds if and only if +$1 = \langle u, x \rangle + \langle v, x \rangle = \langle (u + v), x \rangle$, +where $+$ is a binary addition over $\mathbb{F}_2$. Then, we can rewrite this as +$\langle (u + v), x \rangle = \sum_{(u + v)_i \neq 0} x_i$. Since $u + v \neq +0^n$ from the initial assumption and $x$ is a uniformly random string, the +$\sum_{(u + v)_i \neq 0} x_i = 1$ holds with probability $\frac{1}{2}$. So, +finally, we conclude that $\langle u, x \rangle \neq \langle v, x \rangle$ with +probability $\frac{1}{2}$. \end{proof} \begin{lemma} -The Walsh-Hadamard code is an error-correcting code with distance $\frac{1}{2}$. +The Walsh-Hadamard code $\mathsf{Had}$ is an error-correcting code with distance $\delta = \frac{1}{2}$. \end{lemma} \begin{proof} -Taking two codewords $f(x_1)$ and $f(x_2)$ the distance between them is equal to the number of $y \in \{0, 1\}^n$ where -$\langle x_1, y \rangle \neq \langle x_1, y \rangle$. We know that this happens in the half of the cases, so for a -codeword of size $2^n$ the distance will be $\Delta(f(x_1), f(x_2)) = \frac{2^{n-1}}{2^n} = \frac{1}{2}$. +Taking two codewords $\mathsf{Had}(x_1)$ and $\mathsf{Had}(x_2)$, the distance +between them is equal to the number of such $y \in \{0, 1\}^n$ that $\langle +x_1, y \rangle \neq \langle x_1, y \rangle$. We know that this happens in the +half of the cases, so for a codeword of size $2^n$ the distance will be +$\Delta(\mathsf{Had}(x_1), \mathsf{Had}(x_2)) = \frac{2^{n-1}}{2^n} = +\frac{1}{2}$. \end{proof} This code made a significant impact on the coding theory, mathematics, and theoretical computer science. However, it is impractical for modern applications due to the exponential growth in the size of the codewords. - \subsection{Reed-Solomon code} In information and coding theory, Reed–Solomon ECCs are a group of block error-correcting codes developed by Irving S. Reed and Gustave Solomon in 1960. They are widely used across various @@ -125,7 +193,8 @@ \subsection{Reed-Solomon code} Firstly, let's start from the definition of the input data that differs from binary: \begin{definition} -For some alphabet $\Sigma$ elements $x,y \in \Sigma^n$ we define $\Delta(x, y) = \frac{1}{n}|\{i: x_i \neq y_i\}|$. +For some alphabet $\Sigma$ and elements $x,y \in \Sigma^n$, we define the +relative distance $\Delta(x, y) = \frac{1}{n}\left|\{i \in [n]: x_i \neq y_i\}\right|$. \end{definition} Now we can describe error-correcting codes for the alphabets that differ from binary. Also, let's describe one subclass of the block ECCs to which the Reed-Solomon code belong: @@ -136,65 +205,107 @@ \subsection{Reed-Solomon code} \item[--] For the codeword $c$ and constant $\alpha$ the $\alpha c$ is also a codeword. \end{itemize} \end{definition} + +In the subsequent discussion, we will be interested in the special class of +alphabet: $\Sigma = \mathbb{F}_q$. This way, the considered +\emph{linear error correcting code} $\mathcal{C} \subseteq \mathbb{F}_q^n$ is a +linear subspace of the vector space $\mathbb{F}_q^n$. + \begin{theorem} -Let the \textbf{weight} of a codeword be the number of positions where its value is non-zero. Then, the distance of -the linear error-correcting code is equal to the minimum possible weight of non-zero codeword. +Let the \textbf{weight} $\|c\|_H$ of a codeword $c \in \mathcal{C} \subseteq +\Sigma^n$ be the number of positions where its value is non-zero: $\|c\|_H = +\left|\{i \in [n]: c_i \neq 0\}\right|$. + +Then, the distance $\delta$ of the linear error-correcting code is equal to the +minimum possible weight of non-zero codeword: $\delta = \min_{c \in +\mathcal{C}}\|c\|_H$. \end{theorem} \begin{proof} -For two codewords $c_1, c_2$, the $c_1 - c_2$ is also a codeword. Note, that $\Delta(c_1, c_2) = w(c_1-c_2)$, -where $w(x) = |\{i: x_i \neq 0\}|$ is a weight function and subtraction is done by modulo 2 over binary elements. -Thus, the distance of the linear error-correcting code will be equal to the minimum possible distance between two -non-equal codewords that is also equal to the minimum possible weight of the non-zero codeword: +Note that for two codewords $c_1, c_2 \in \mathcal{C}$, the $c_1 - c_2$ is also +a codeword from $\mathcal{C}$. Then, \begin{equation*} - \delta = \min_{c_1 \neq c_2} \Delta(c_1, c_2) = \min_{c_1 \neq c_2} w(c_1 - c_2) = \min_{c \neq 0} w(c) + \delta = \min_{c_1 \neq c_2 \in \mathcal{C}} \Delta(c_1, c_2) = \min_{c_1 \neq c_2} \|c_1 - c_2\|_H = \min_{c \neq 0} \|c\|_H \end{equation*} + +Note, that the Walsh-Hadamard code is also a linear code, so we can prove its +distance using this approach as well. \end{proof} -Note, that the Walsh-Hadamard code is also a linear code, so we can prove its distance using this approach as well. +Now, we shift our focus towards one of the core zk-STARK components: the +Reed-Solomon code. This code is a linear error-correcting code that operates +over the certain finite field $\mathbb{F}$ and polynomials $\mathbb{F}[X]$. The +definition is following. \begin{definition}[Reed-Solomon code] -For a field $\mathbb{F}$ and numbers $0 < n \leq m < |\mathbb{F}|$ the \textbf{Reed-Solomon code} from -$\mathbb{F}^n \rightarrow \mathbb{F}^m$ is a function that takes $n-1$ degree polynomial $A(x) \in \mathbb{F}[x]$ -and outputs its evaluation over $m$ points $f_0,...,f_{m-1} \in \mathbb{F}$. +Let $\Omega \subseteq \mathbb{F}$ be some \emph{evaluation domain} with a rate +parameter $\rho \in (0,1]$. Formally, the \textbf{Reed-Solomon Code} +$\mathsf{RS}[\mathbb{F},\Omega,\rho]$ is defined as the space of functions $f: +\Omega \to \mathbb{F}$ that are evaluations of polynomials of degree up to +$\rho|\Omega|$. Put much more simply, the codeword of the Reed-Solomon code is an +evaluation of a polynomial of degree less than $\rho|\Omega|$ over the points of the +evaluation domain $\Omega$: +\begin{equation*} + \mathsf{RS}[\mathbb{F},\Omega,\rho] = \left\{f(x)\big|_{x \in \Omega}: f \in \mathbb{F}^{(\leq \rho |\Omega|)}[x]\right\}, +\end{equation*} + +where we abuse the notation to denote $f(x)\big|_{x \in \Omega} = \left(f(\omega)\right)_{\omega \in \Omega}$. \end{definition} + +The definition is very hard to grasp on its own, so we help you with +an example. + +\begin{example} + Suppose $\mathbb{F} = \mathbb{F}_7$ and let evaluation domain be $\Omega = + \{1,2,5,6\}$. Then, the Reed-Solomon code $\mathsf{RS}[\mathbb{F}_7, \Omega, + \frac{1}{2}]$ is a set of all evaluations of polynomials of degree not + exceeding $4 \cdot \frac{1}{2}=2$ (that is, quadratic) over the points + $\{1,2,5,6\}$. For example, for $f(x) = (x+1)^2 \in \mathbb{F}^{(\leq 2)}_7$ + the codeword is $(f(1),f(2),f(5),f(6)) = (4, 2, 1, 0) \in + \mathsf{RS}[\mathbb{F}_7, \Omega, \frac{1}{2}]$. +\end{example} + +\begin{remark} + Typically, we assume that $\rho|\Omega|$ is an integer, so we omit rounding + operations in the following discussion. +\end{remark} + +Let us now estimate some parameters of the Reed-Solomon code. \begin{lemma} -The Reed-Solomon code has distance of $1 - \frac{n-1}{m}$ +The Reed-Solomon code has a distance of $1 - \rho$. \end{lemma} \begin{proof} -The word is a polynomial of degree less then $n$ so it can have no more then $n-1$ roots. The codeword is the -evaluation of this polynomial at $m > n$ distinct points, meaning it must contain at least $m-(n-1)$ non-zero -values (since at most $n-1$ points in $f$ can be roots of our word polynomial). Since the Reed-Solomon code is a -linear code, we can use its property to define its distance as $\delta =\frac{m - n + 1}{n} = 1 - \frac{n-1}{m}$. +Let $N = |\Omega|$. The word is a polynomial of degree up to $\rho N$ so it can +have at most $\rho N$ roots. The codeword is the evaluation of this polynomial +at $N$ distinct points, meaning it must contain at least $N-\rho N=(1-\rho)N$ +non-zero values (otherwise, the polynomial would have more than $\rho N$ roots +and thus the higher degree than $\rho N$). Since the Reed-Solomon code is a +linear code, we conclude that the distance is $(1-\rho)N/N = 1-\rho$. \end{proof} -By following the ``Unisolvence theorem'' we know that to recover $n-1$ degree polynomial we need at least $n$ points -for interpolation. Because the Reed-Solomon code performs polynomial evaluation over $m > n$ points we can still -interpolate (or decode) the polynomial even if some points are corrupted during data transmission. - +By following the ``Unisolvence theorem'', we know that recovering $\rho N$ +degree polynomial requires at least $\rho N+1$ points for interpolation. Because +the Reed-Solomon code performs polynomial evaluation over $N > \rho N$ points, +we can still interpolate (or decode) the polynomial even if some points are +corrupted during the data transmission. \begin{theorem} -There exists a polynomial-time (depending on the codeword size) algorithm with the following properties: -\begin{itemize} - \item[--] \verb|Input|: a list of pairs $(a_i, b_i)|^{m-1}_0$ of elements in $\mathbb{F}$ such that for - $\hat{t} > \frac{m}{2} + \frac{n}{2}$ of them $G(a_i) = b_i$ for some unique polynomial $G(x)$ with $\deg G(x) = n-1$. - \item[--] \verb|Output|: a polynomial $G(x)$ with $\deg G(x) = n-1$. -\end{itemize} +Suppose for a given list of pairs $\mathcal{D} = \{x_j, y_j\}_{j \in [N]}$ there +exists a unique polynomial $f(x) \in \mathbb{F}^{(\leq \rho N)}[x]$ such that +$f(x_j) = y_j$ for at least $\frac{1+\rho}{2}N$ of them. Then, there exists a +\textit{polynomial-time algorithm} $\mathsf{Dec}$ (in $O(N^{\gamma})$ for some +constant $\gamma>1$), extracting the polynomial: $\mathsf{Dec}(\mathcal{D}) = +f$. \end{theorem} \begin{proof} -The assumption $\hat{t} > \frac{m}{2} + \frac{n}{2}$ can be transformed into the corresponding constraint on the number -of possible errors $t$: -\begin{equation*} - t = m - \hat{t} < m - \frac{m}{2} - \frac{n}{2} = \frac{m}{2} - \frac{n}{2} -\end{equation*} -Let's also evaluate a necessary constraint that will be useful in the algorithm below: -\begin{gather*} - t < \frac{m}{2} - \frac{n}{2}\\ - 2t < m - n\\ - m > 2t+n -\end{gather*} -So, we know the lower bound of the codeword size depending on the word and number of errors: $m \geq 2t+n+1$. +The assumption that at least $\frac{1+\rho}{2}N$ points must be +``interpolatable'' for some $f(x) \in \mathbb{F}^{(\leq \rho N)}[x]$ can be +transformed into the corresponding constraint on the number of possible errors +$t$: indeed, if $m$ is the number of such points, then $t = N - m < +\frac{1-\rho}{2}N$. Alternatively, this implies $N > 2t+N\rho$, which will be +useful later. That being said, we know the lower bound of the codeword size +depending on the word and number of errors: $N \geq 2t+\rho N+1$. \begin{tcolorbox}[title=Berlekamp-Welch decoding, breakable, @@ -208,36 +319,50 @@ \subsection{Reed-Solomon code} colback=blue!20!white, colupper=blue!75!gray} ] - So, $G(a_i) = b_i$ for at least $\hat{t}$ of $m$ pairs. We also know that $\deg G(x) = n-1$ and $n < t < m$. - It means that we already can recover polynomial but we firstly have to deal with an errors. + \textbf{Assume:} $f(x_j) = y_j$ for at least $\frac{1+\rho}{2}N$ of $N$ + pairs with $f \in \mathbb{F}^{(\leq \rho N)}[x]$. + + \textit{Comment:} Since $t/N < 1-\rho$, recovering polynomial $f$ is + possible, but we need to somehow know which points are corrupted. - \tcbsubtitle{$\mathsf{Decoding}(\mathbb{F}, n, m, (a_i, b_i)|^{m-1}_0)$} + \tcbsubtitle{$\mathsf{Dec}(\mathsf{RS}[\mathbb{F},\Omega,\rho], \{x_j, + y_j\}_{j \in [N]})$} \begin{itemize}[label=\ding{51}] - \item Let's put an \textbf{error polynomial} $E(X)$ a polynomial which has roots at the error points. - \item So, $\deg E(x) = t < \frac{m}{2} - \frac{n}{2}$. - \item Our algorithm is based on the following equation: $$C(a_i) = G(a_i)\cdot E(a_i), i \in 0..m-1$$ - where $C(x)$ is an arbitrary polynomial that we are going to find. - \item Note, that $\deg C(x) = \deg G(x) + \deg E(x) = n - 1 + t$ - \item Then, by solving the equation $C(a_i) = b_i\cdot E(a_i), i \in 0..m-1$ we can find $C(x)$ and $E(x)$. - \item This can be considered as a set of $m$ linear equations where we have unknown $n+t$ coefficients - of $C(x)$ and $t+1$ coefficient of $E(x)$, so it can be solved via linear algebra while $m \geq 2t+n+1$. - \item Finally, we put $G(x) = \frac{C(x)}{E(x)}$ + \item Let's put an \textbf{error polynomial} $e(X)$ a polynomial which + has roots at the error points. This implies $\deg e(x) = t < + \frac{1-\rho}{2}N$. + \item Our algorithm is based on the following equation: + \begin{equation*} + g(x_j) = f(x_j)\cdot e(x_j), \; j \in [m], + \end{equation*} + where $g(x)$ is an arbitrary polynomial that we are going to find. + \item Note, that $\deg g = \deg f + \deg e = \rho N + t - 1$. + \item By solving the equation $g(x_j) = y_j\cdot e(x_j)$, $j \in [m]$, + we can find $g(x)$ and $e(x)$. Indeed, this can be considered as a set + of $N$ linear equations where we have unknown $\rho N+t$ coefficients of + $g(x)$ and $t+1$ coefficients of $e(x)$. This, it can be solved via + linear algebra methods as long as $N \geq 2t+\rho N+1$. + \item Finally, we set $f(x) = g(x)/e(x)$. \end{itemize} \end{tcolorbox} -The last equation follows from the observation that polynomial $C(x) - G(x)\cdot E(x)$ is zero in $\hat{t}$ points -while it has degree $n + t - 1$. It is easy to prove that the polynomial degree less then the number of it's roots, -so $C(x) - G(x)\cdot E(x)$ is zero for each $x$. That is why from $C(x) = E(x)G(x)$ follows that $C(x)$ is divisible -on $E(x)$. +The last equation follows from the observation that polynomial $g(x) - f(x)e(x)$ +is zero in $m \leq \frac{1+\rho}{2}N$ points while it has degree $\rho N + t - +1$. It is easy to prove that the polynomial degree less then the number of its +roots, so $g(x) - f(x)e(x)$ is zero for each $x$. That is why from $g(x) = +e(x)f(x)$ it follows that $g(x)$ is divisible by $e(x)$. \end{proof} \subsubsection{Reed-Solomon(255,223) implementation} -The example implementation aims to help reader to understand how the encoding and decoding looks like. The -Reed-Solomon code with $m=255, n=223$ is widely used in communication and storage systems, including QR codes, CDs, -DVDs, and deep-space communication. It operates over $\mathbb{GF}(256)$ (the finite field of $256$ elements) -and encodes data as polynomials evaluated at different field elements. Following our theorem, the number of errors -is constrained as $t < \frac{m}{2} - \frac{n}{2}$ which means that $t < 16$. For our implementation we take $t = 15$. +The example implementation aims to help reader to understand how the encoding +and decoding looks like in practice. The Reed-Solomon code with $m=255, n=223$ +is widely used in communication and storage systems, including QR codes, CDs, +DVDs, and deep-space communication. It operates over $\mathbb{F}_{256}$ (the +finite field of $256$ elements) and encodes data as polynomials evaluated at +different field elements. Following our theorem, the number of errors is +constrained as $t < \frac{1-\rho}{2}N$ which means that $t < 16$. For our +implementation we take $t = 15$. Let's start from the basic parameters definition: \begin{lstlisting}[language=Python,numbers=none] @@ -248,13 +373,15 @@ \subsubsection{Reed-Solomon(255,223) implementation} t = 15 # The number of possible errors distance = (m - n + 1)/m \end{lstlisting} -The \verb|distance| then is equal to $\frac{11}{85}$. Let's sample a random message that will be an information -polynomial of degree $n=223$ (this messsage can be obtained by the interpolation of some useful data): +The \verb|distance| then is equal to $\frac{11}{85}$. Let's sample a random +message that will be an information polynomial of degree $\rho N=223$ (this +messsage can be obtained by the interpolation of some useful data): \begin{lstlisting}[language=Python,numbers=none] word = sum(F.random_element() * x^i for i in range(n)) \end{lstlisting} -We start encoding from defining the evaluation elements that will be all non-zero elements of $\mathbb{GF}(256)$. -Then, our codeword will be the $m = 255$ evaluations of our information polynomial. +We start encoding from defining the evaluation elements that will be all +non-zero elements of $\mathbb{F}_{256}$. Then, our codeword will be the $N = +255$ evaluations of our information polynomial. \begin{lstlisting}[language=Python,numbers=none] # All non zero elements in F. Order(F) - 1 = 255 f = [f_i for f_i in F if f_i != 0] @@ -262,10 +389,12 @@ \subsubsection{Reed-Solomon(255,223) implementation} codeword = [word(f_i) for f_i in f] assert len(codeword) == m \end{lstlisting} -To decode the codeword we should solve the system of $m$ equations $C(x_i) - b_i \cdot E(x_i) = 0$. Note, that this system -is homogeneous, so the trivial solution (all zeros) is always a solution. To find a non-zero solution we impose a -normalization condition, such as setting the leading coefficient of $E(x)$ to $1$. This turns our original equation -into the $C(x_i) - b_i \cdot E(x_i) = b_i \cdot x_i^deg_E$. +To decode the codeword we should solve the system of $N$ equations $g(x_j) - b_j +\cdot e(x_j) = 0$. Note, that this system is homogeneous, so the trivial +solution (all zeros) is always a solution. To find a non-zero solution we impose +a normalization condition, such as setting the leading coefficient of $e(x)$ to +$1$. This turns our original equation into the $g(x_j) - b_j \cdot e(x_j) = b_j +\cdot x_j^{\deg e}$. \begin{lstlisting}[language=Python,numbers=none] deg_C = 237 # deg = n - 1 + t deg_E = 15 # deg = t @@ -285,8 +414,9 @@ \subsubsection{Reed-Solomon(255,223) implementation} solution = M.solve_right(rhs) assert len(solution) == deg_C + deg_E + 1 \end{lstlisting} -Finally, we can recover the original information polynomial $G(x) = \frac{C(x)}{E(x)}$. We also should not forget to -add the normalized leading coefficient of $E(x)$. + +Finally, we can recover the original information polynomial $f \gets g/e$. We +also should not forget to add the normalized leading coefficient of $e(x)$. \begin{lstlisting}[language=Python,numbers=none] C = sum(solution[i] * x^i for i in range(deg_C + 1)) E = sum(solution[deg_C + 1 + i] * x^i for i in range(deg_E)) @@ -296,7 +426,7 @@ \subsubsection{Reed-Solomon(255,223) implementation} \end{lstlisting} To discrover the error correcting properties of the presented code you can add the following snippet after the -codeword calculation. It changes first $t$ elements of codeword to the random $\mathbb{GF}(256)$ elements. If you try +codeword calculation. It changes first $t$ elements of codeword to the random $\mathbb{F}_{256}$ elements. If you try to change more elements then the linear system solving procedure will fail. \begin{lstlisting}[language=Python,numbers=none] for i in range(t): diff --git a/preface/acknowledgements.pdf b/preface/acknowledgements.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a7aca891390a8e227c50ca7189229cccff122894 GIT binary patch literal 25533 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 literal 0 HcmV?d00001 diff --git a/preface/preface.pdf b/preface/preface.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f58438e5ec5e5a1108f12880a4f0228497974b85 GIT binary patch literal 42345 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 literal 0 HcmV?d00001 diff --git a/zkdl-template.cls b/zkdl-template.cls index 679a4f7..fe1fb20 100644 --- a/zkdl-template.cls +++ b/zkdl-template.cls @@ -234,7 +234,7 @@ \tcolorboxenvironment{theorem}{ breakable, boxrule=0pt, boxsep=0pt, colback={blue!10},left=8pt,right=8pt,enhanced jigsaw, borderline west={2pt}{0pt}{blue},sharp corners,before skip=10pt,after skip=10pt} \tcolorboxenvironment{lemma}{ breakable, boxrule=0pt, boxsep=0pt, colback={Cyan!10},left=8pt,right=8pt,enhanced jigsaw, borderline west={2pt}{0pt}{Cyan},sharp corners,before skip=10pt,after skip=10pt} \tcolorboxenvironment{corollary}{ breakable, boxrule=0pt, boxsep=0pt, colback={violet!10},left=8pt,right=8pt,enhanced jigsaw, borderline west={2pt}{0pt}{violet},sharp corners,before skip=10pt,after skip=10pt} -\tcolorboxenvironment{proof}{ breakable, boxrule=0pt, boxsep=0pt, blanker,borderline west={2pt}{0pt}{CadetBlue!80!white},left=8pt,right=8pt,sharp corners,before skip=10pt,after skip=10pt} +%\tcolorboxenvironment{proof}{ breakable, boxrule=0pt, boxsep=0pt, blanker,borderline west={2pt}{0pt}{CadetBlue!80!white},left=8pt,right=8pt,sharp corners,before skip=10pt,after skip=10pt} \tcolorboxenvironment{remark}{ breakable, boxrule=0pt, boxsep=0pt, blanker,borderline west={2pt}{0pt}{Green},left=8pt,right=8pt,before skip=10pt,after skip=10pt} \tcolorboxenvironment{remarks}{ breakable, boxrule=0pt, boxsep=0pt, blanker,borderline west={2pt}{0pt}{Green},left=8pt,right=8pt,before skip=10pt,after skip=10pt} \tcolorboxenvironment{example}{ breakable, boxrule=0pt, boxsep=0pt, colback={Gray!10},left=8pt,right=8pt,enhanced jigsaw, borderline west={2pt}{0pt}{Gray},sharp corners,before skip=10pt,after skip=10pt} From c54e73c7ba9ce7942a163464877f2c086f0349cf Mon Sep 17 00:00:00 2001 From: olegfomenko Date: Tue, 11 Mar 2025 12:52:50 +0200 Subject: [PATCH 5/9] refactored RS code --- lectures/14-ecc.tex | 163 ++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 75 deletions(-) diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index ecb597a..6f33e16 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -234,7 +234,7 @@ \subsection{Reed-Solomon code} Now, we shift our focus towards one of the core zk-STARK components: the Reed-Solomon code. This code is a linear error-correcting code that operates over the certain finite field $\mathbb{F}$ and polynomials $\mathbb{F}[X]$. The -definition is following. +definition is following: \begin{definition}[Reed-Solomon code] Let $\Omega \subseteq \mathbb{F}$ be some \emph{evaluation domain} with a rate @@ -250,10 +250,8 @@ \subsection{Reed-Solomon code} where we abuse the notation to denote $f(x)\big|_{x \in \Omega} = \left(f(\omega)\right)_{\omega \in \Omega}$. \end{definition} - The definition is very hard to grasp on its own, so we help you with an example. - \begin{example} Suppose $\mathbb{F} = \mathbb{F}_7$ and let evaluation domain be $\Omega = \{1,2,5,6\}$. Then, the Reed-Solomon code $\mathsf{RS}[\mathbb{F}_7, \Omega, @@ -269,43 +267,67 @@ \subsection{Reed-Solomon code} operations in the following discussion. \end{remark} +Additionally, the presented original definition can be simplified as follows: +\begin{definition}[Reed-Solomon code] +For a field $\mathbb{F}$ and numbers $0 < n \leq m < |\mathbb{F}|$ the +\textbf{Reed-Solomon Code} is a function $\mathsf{RS}: \mathbb{F}^n \rightarrow \mathbb{F}^m$ that for each +$n-1$ degree polynomial $A(x) \in \mathbb{F}[x]$ outputs its evaluation over $m$ points $f_0,...,f_{m-1} \in \mathbb{F}$. + +We can show the equality to the original definition if we put $\rho = \frac{n - 1}{m}$ and $\Omega = \mathbb{F}^m$: + +\begin{equation*} + \mathsf{RS}\Big[\mathbb{F},\Omega = \mathbb{F}^m,\rho = \frac{n - 1}{m}\Big] = \left\{A(x)\big|_{x \in \Omega }: A \in \mathbb{F}^{(\leq n -1)}[x]\right\}, +\end{equation*} +\end{definition} + +\begin{remark} +For the future definitions and theorems we will use the simplified definition. All used notations can be +redefined by replacing $m = |\Omega|$, $F^m = \Omega$ and $n - 1 = \rho |\Omega|$. +\end{remark} + Let us now estimate some parameters of the Reed-Solomon code. \begin{lemma} -The Reed-Solomon code has a distance of $1 - \rho$. +The Reed-Solomon code has a distance of $1 - \frac{n-1}{m}$. \end{lemma} \begin{proof} -Let $N = |\Omega|$. The word is a polynomial of degree up to $\rho N$ so it can -have at most $\rho N$ roots. The codeword is the evaluation of this polynomial -at $N$ distinct points, meaning it must contain at least $N-\rho N=(1-\rho)N$ -non-zero values (otherwise, the polynomial would have more than $\rho N$ roots -and thus the higher degree than $\rho N$). Since the Reed-Solomon code is a -linear code, we conclude that the distance is $(1-\rho)N/N = 1-\rho$. +The word is a polynomial of degree less then $n$ so it can have no more then $n-1$ roots. +The codeword is the evaluation of this polynomial at $m > n$ distinct points, meaning it +must contain at least $m-(n-1)$ non-zero values (since at most $n-1$ points in $f$ can be +roots of our word polynomial). Since the Reed-Solomon code is a linear code, we can use its +property to define its distance as +$\delta =\frac{m - n + 1}{n} = 1 - \frac{n-1}{m}$. $\triangle$ \end{proof} -By following the ``Unisolvence theorem'', we know that recovering $\rho N$ -degree polynomial requires at least $\rho N+1$ points for interpolation. Because -the Reed-Solomon code performs polynomial evaluation over $N > \rho N$ points, -we can still interpolate (or decode) the polynomial even if some points are -corrupted during the data transmission. +By following the ``Unisolvence theorem'' we know that to recover $n-1$ degree polynomial +we need at least $n$ points for interpolation. Because the Reed-Solomon code performs polynomial +evaluation over $m > n$ points we can still interpolate (or decode) the polynomial even if some +points are corrupted during data transmission. \begin{theorem} -Suppose for a given list of pairs $\mathcal{D} = \{x_j, y_j\}_{j \in [N]}$ there -exists a unique polynomial $f(x) \in \mathbb{F}^{(\leq \rho N)}[x]$ such that -$f(x_j) = y_j$ for at least $\frac{1+\rho}{2}N$ of them. Then, there exists a +Suppose for a given list of pairs $\mathcal{D} = (a_i, b_i)\big|^{m-1}_0$ of elements +in $\mathbb{F}$ there exists a unique polynomial $G(x) \in \mathbb{F}^{(\leq n-1)}[x]$ such that +$G(a_j) = b_j$ for at least $\hat{t} > \frac{m}{2} + \frac{n}{2}$ of them. Then, there exists a \textit{polynomial-time algorithm} $\mathsf{Dec}$ (in $O(N^{\gamma})$ for some constant $\gamma>1$), extracting the polynomial: $\mathsf{Dec}(\mathcal{D}) = -f$. +G$. \end{theorem} \begin{proof} -The assumption that at least $\frac{1+\rho}{2}N$ points must be -``interpolatable'' for some $f(x) \in \mathbb{F}^{(\leq \rho N)}[x]$ can be -transformed into the corresponding constraint on the number of possible errors -$t$: indeed, if $m$ is the number of such points, then $t = N - m < -\frac{1-\rho}{2}N$. Alternatively, this implies $N > 2t+N\rho$, which will be -useful later. That being said, we know the lower bound of the codeword size -depending on the word and number of errors: $N \geq 2t+\rho N+1$. +The assumption that at least $\hat{t} > \frac{m}{2} + \frac{n}{2}$ points must be +``interpolatable'' for some $G(x) \in \mathbb{F}^{(\leq n-1)}[x]$ can be transformed into the +corresponding constraint on the number of possible errors $t$: +\begin{equation*} + t = m - \hat{t} < m - \frac{m}{2} - \frac{n}{2} = \frac{m}{2} - \frac{n}{2} +\end{equation*} +Alternatively, this defines a necessary constraint that will be useful later: +\begin{gather*} + t < \frac{m}{2} - \frac{n}{2}\\ + 2t < m - n\\ + m > 2t+n +\end{gather*} +hat being said, we know the lower bound of the codeword size +depending on the word and number of errors: $m \geq 2t+n+1$. \begin{tcolorbox}[title=Berlekamp-Welch decoding, breakable, @@ -319,50 +341,47 @@ \subsection{Reed-Solomon code} colback=blue!20!white, colupper=blue!75!gray} ] - \textbf{Assume:} $f(x_j) = y_j$ for at least $\frac{1+\rho}{2}N$ of $N$ - pairs with $f \in \mathbb{F}^{(\leq \rho N)}[x]$. - - \textit{Comment:} Since $t/N < 1-\rho$, recovering polynomial $f$ is - possible, but we need to somehow know which points are corrupted. + So, $G(a_i) = b_i$ for at least $\hat{t}$ of $m$ pairs in $\mathcal{D}$. We also know that + $\deg G(x) = n-1$ and $n < t < m$. It means that we already can recover polynomial + but we firstly have to deal with an errors. + + \tcbsubtitle{$\mathsf{Dec}(\mathcal{D})$} - \tcbsubtitle{$\mathsf{Dec}(\mathsf{RS}[\mathbb{F},\Omega,\rho], \{x_j, - y_j\}_{j \in [N]})$} - \begin{itemize}[label=\ding{51}] - \item Let's put an \textbf{error polynomial} $e(X)$ a polynomial which - has roots at the error points. This implies $\deg e(x) = t < - \frac{1-\rho}{2}N$. + \item Let's put an \textbf{error polynomial} $E(X)$ a polynomial which has + roots at the error points. + \item So, $\deg E(x) = t < \frac{m}{2} - \frac{n}{2}$. \item Our algorithm is based on the following equation: - \begin{equation*} - g(x_j) = f(x_j)\cdot e(x_j), \; j \in [m], - \end{equation*} - where $g(x)$ is an arbitrary polynomial that we are going to find. - \item Note, that $\deg g = \deg f + \deg e = \rho N + t - 1$. - \item By solving the equation $g(x_j) = y_j\cdot e(x_j)$, $j \in [m]$, - we can find $g(x)$ and $e(x)$. Indeed, this can be considered as a set - of $N$ linear equations where we have unknown $\rho N+t$ coefficients of - $g(x)$ and $t+1$ coefficients of $e(x)$. This, it can be solved via - linear algebra methods as long as $N \geq 2t+\rho N+1$. - \item Finally, we set $f(x) = g(x)/e(x)$. + $$C(a_i) = G(a_i)\cdot E(a_i), i \in 0..m-1$$ + where $C(x)$ is an arbitrary polynomial that we are going to find. + \item Note, that $\deg C(x) = \deg G(x) + \deg E(x) = n - 1 + t$ + \item Then, by solving the equation $C(a_i) = b_i\cdot E(a_i), i \in 0..m-1$ + we can find $C(x)$ and $E(x)$. + \item This can be considered as a set of $m$ linear equations where we have + unknown $n+t$ coefficients + of $C(x)$ and $t+1$ coefficient of $E(x)$, so it can be solved via linear + algebra while $m \geq 2t+n+1$. + \item Finally, we put $G(x) = \frac{C(x)}{E(x)}$ \end{itemize} - + \end{tcolorbox} -The last equation follows from the observation that polynomial $g(x) - f(x)e(x)$ -is zero in $m \leq \frac{1+\rho}{2}N$ points while it has degree $\rho N + t - -1$. It is easy to prove that the polynomial degree less then the number of its -roots, so $g(x) - f(x)e(x)$ is zero for each $x$. That is why from $g(x) = -e(x)f(x)$ it follows that $g(x)$ is divisible by $e(x)$. +The last equation follows from the observation that polynomial +$C(x) - G(x)\cdot E(x)$ is zero in $\hat{t}$ points +while it has degree $n + t - 1$. It is easy to prove that the polynomial degree +less then the number of it's roots, so $C(x) - G(x)\cdot E(x)$ is zero for +each $x$. That is why from $C(x) = E(x)G(x)$ follows that $C(x)$ is divisible +on $E(x)$. \end{proof} \subsubsection{Reed-Solomon(255,223) implementation} The example implementation aims to help reader to understand how the encoding -and decoding looks like in practice. The Reed-Solomon code with $m=255, n=223$ +and decoding look like in practice. The Reed-Solomon code with $m=255, n=223$ is widely used in communication and storage systems, including QR codes, CDs, DVDs, and deep-space communication. It operates over $\mathbb{F}_{256}$ (the finite field of $256$ elements) and encodes data as polynomials evaluated at -different field elements. Following our theorem, the number of errors is -constrained as $t < \frac{1-\rho}{2}N$ which means that $t < 16$. For our -implementation we take $t = 15$. +different field elements. Following our theorem, the number of errors +is constrained as $t < \frac{m}{2} - \frac{n}{2}$ which means that $t < 16$. +For our implementation we take $t = 15$. Let's start from the basic parameters definition: \begin{lstlisting}[language=Python,numbers=none] @@ -373,15 +392,13 @@ \subsubsection{Reed-Solomon(255,223) implementation} t = 15 # The number of possible errors distance = (m - n + 1)/m \end{lstlisting} -The \verb|distance| then is equal to $\frac{11}{85}$. Let's sample a random -message that will be an information polynomial of degree $\rho N=223$ (this -messsage can be obtained by the interpolation of some useful data): +The \verb|distance| then is equal to $\frac{11}{85}$. Let's sample a random message that will be an information +polynomial of degree $n=223$ (this messsage can be obtained by the interpolation of some useful data): \begin{lstlisting}[language=Python,numbers=none] word = sum(F.random_element() * x^i for i in range(n)) \end{lstlisting} -We start encoding from defining the evaluation elements that will be all -non-zero elements of $\mathbb{F}_{256}$. Then, our codeword will be the $N = -255$ evaluations of our information polynomial. +We start encoding from defining the evaluation elements that will be all non-zero elements of $\mathbb{GF}(256)$. +Then, our codeword will be the $m = 255$ evaluations of our information polynomial. \begin{lstlisting}[language=Python,numbers=none] # All non zero elements in F. Order(F) - 1 = 255 f = [f_i for f_i in F if f_i != 0] @@ -389,12 +406,10 @@ \subsubsection{Reed-Solomon(255,223) implementation} codeword = [word(f_i) for f_i in f] assert len(codeword) == m \end{lstlisting} -To decode the codeword we should solve the system of $N$ equations $g(x_j) - b_j -\cdot e(x_j) = 0$. Note, that this system is homogeneous, so the trivial -solution (all zeros) is always a solution. To find a non-zero solution we impose -a normalization condition, such as setting the leading coefficient of $e(x)$ to -$1$. This turns our original equation into the $g(x_j) - b_j \cdot e(x_j) = b_j -\cdot x_j^{\deg e}$. +To decode the codeword we should solve the system of $m$ equations $C(x_i) - b_i \cdot E(x_i) = 0$. Note, that this system +is homogeneous, so the trivial solution (all zeros) is always a solution. To find a non-zero solution we impose a +normalization condition, such as setting the leading coefficient of $E(x)$ to $1$. This turns our original equation +into the $C(x_i) - b_i \cdot E(x_i) = b_i \cdot x_i^{\deg(E)}$. \begin{lstlisting}[language=Python,numbers=none] deg_C = 237 # deg = n - 1 + t deg_E = 15 # deg = t @@ -414,9 +429,8 @@ \subsubsection{Reed-Solomon(255,223) implementation} solution = M.solve_right(rhs) assert len(solution) == deg_C + deg_E + 1 \end{lstlisting} - -Finally, we can recover the original information polynomial $f \gets g/e$. We -also should not forget to add the normalized leading coefficient of $e(x)$. +Finally, we can recover the original information polynomial $G(x) = \frac{C(x)}{E(x)}$. We also should not forget to +add the normalized leading coefficient of $E(x)$. \begin{lstlisting}[language=Python,numbers=none] C = sum(solution[i] * x^i for i in range(deg_C + 1)) E = sum(solution[deg_C + 1 + i] * x^i for i in range(deg_E)) @@ -426,14 +440,13 @@ \subsubsection{Reed-Solomon(255,223) implementation} \end{lstlisting} To discrover the error correcting properties of the presented code you can add the following snippet after the -codeword calculation. It changes first $t$ elements of codeword to the random $\mathbb{F}_{256}$ elements. If you try +codeword calculation. It changes first $t$ elements of codeword to the random $\mathbb{GF}(256)$ elements. If you try to change more elements then the linear system solving procedure will fail. \begin{lstlisting}[language=Python,numbers=none] for i in range(t): codeword[i] = F.random_element() \end{lstlisting} - \subsection{Convolutional codes} A convolutional code is an ECC that utilizes the following properties: \begin{enumerate} From 250025a0dfdaf8bc93061636693fe52318ae02f7 Mon Sep 17 00:00:00 2001 From: olegfomenko Date: Tue, 11 Mar 2025 13:46:42 +0200 Subject: [PATCH 6/9] refactored RS code: roll back to the rho notation --- lectures/14-ecc.tex | 76 ++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index 6f33e16..504a667 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -281,8 +281,9 @@ \subsection{Reed-Solomon code} \end{definition} \begin{remark} -For the future definitions and theorems we will use the simplified definition. All used notations can be -redefined by replacing $m = |\Omega|$, $F^m = \Omega$ and $n - 1 = \rho |\Omega|$. +For the future definitions and theorems we will use the original definition. However, a simplified +nonation can be obtained by replacing the +$\Omega = F^m$, $|\Omega| = m$, and $\rho |\Omega| = n - 1$. \end{remark} Let us now estimate some parameters of the Reed-Solomon code. @@ -291,43 +292,43 @@ \subsection{Reed-Solomon code} The Reed-Solomon code has a distance of $1 - \frac{n-1}{m}$. \end{lemma} \begin{proof} -The word is a polynomial of degree less then $n$ so it can have no more then $n-1$ roots. -The codeword is the evaluation of this polynomial at $m > n$ distinct points, meaning it -must contain at least $m-(n-1)$ non-zero values (since at most $n-1$ points in $f$ can be -roots of our word polynomial). Since the Reed-Solomon code is a linear code, we can use its -property to define its distance as -$\delta =\frac{m - n + 1}{n} = 1 - \frac{n-1}{m}$. $\triangle$ +Let $m = |\Omega|$. The word is a polynomial of degree up to $\rho \cdot m$ so it can have no more then +$\rho \cdot m$ roots. The codeword is the evaluation of this polynomial at $m > \rho \cdot m$ distinct points, +meaning it must contain at least $m-\rho \cdot m =(1-\rho)m$ non-zero values (since at most +$\rho \cdot m$ points can be the roots of our word polynomial). Since the Reed-Solomon code is a +linear code, we can use its property to define its distance as +$\frac{(1-\rho)m}{m} = 1-\rho$. \end{proof} -By following the ``Unisolvence theorem'' we know that to recover $n-1$ degree polynomial -we need at least $n$ points for interpolation. Because the Reed-Solomon code performs polynomial -evaluation over $m > n$ points we can still interpolate (or decode) the polynomial even if some -points are corrupted during data transmission. +By following the ``Unisolvence theorem'' we know that to recover $\rho \cdot m$ degree polynomial +we need at least $\rho \cdot m + 1$ points for interpolation. Because the Reed-Solomon code +performs polynomial evaluation over $m > \rho \cdot m$ points we can still interpolate (or decode) +the polynomial even if some points are corrupted during data transmission. \begin{theorem} -Suppose for a given list of pairs $\mathcal{D} = (a_i, b_i)\big|^{m-1}_0$ of elements -in $\mathbb{F}$ there exists a unique polynomial $G(x) \in \mathbb{F}^{(\leq n-1)}[x]$ such that -$G(a_j) = b_j$ for at least $\hat{t} > \frac{m}{2} + \frac{n}{2}$ of them. Then, there exists a +Suppose for a given list of pairs $\mathcal{D} = \{(x_i, y_i)\}_{i \in [m]}$ of elements +in $\mathbb{F}$ there exists a unique polynomial $G(x) \in \mathbb{F}^{(\leq \rho\cdot m)}[x]$ such that +$G(x_j) = y_j$ for at least $\hat{t} > \frac{m}{2} + \frac{\rho\cdot m + 1}{2}$ of them. Then, there exists a \textit{polynomial-time algorithm} $\mathsf{Dec}$ (in $O(N^{\gamma})$ for some constant $\gamma>1$), extracting the polynomial: $\mathsf{Dec}(\mathcal{D}) = G$. \end{theorem} \begin{proof} -The assumption that at least $\hat{t} > \frac{m}{2} + \frac{n}{2}$ points must be -``interpolatable'' for some $G(x) \in \mathbb{F}^{(\leq n-1)}[x]$ can be transformed into the +The assumption that at least $\hat{t} > \frac{m}{2} + \frac{\rho\cdot m + 1}{2}$ points must be +``interpolatable'' for some $G(x) \in \mathbb{F}^{(\leq \rho\cdot m)}[x]$ can be transformed into the corresponding constraint on the number of possible errors $t$: \begin{equation*} - t = m - \hat{t} < m - \frac{m}{2} - \frac{n}{2} = \frac{m}{2} - \frac{n}{2} + t = m - \hat{t} < m - \frac{m}{2} - \frac{\rho\cdot m + 1}{2} = \frac{m}{2} - \frac{\rho\cdot m + 1}{2} \end{equation*} Alternatively, this defines a necessary constraint that will be useful later: \begin{gather*} - t < \frac{m}{2} - \frac{n}{2}\\ - 2t < m - n\\ - m > 2t+n + t < \frac{m}{2} - \frac{\rho\cdot m + 1}{2}\\ + 2t < m - \rho\cdot m + 1\\ + m > 2t + \rho\cdot m + 1 \end{gather*} -hat being said, we know the lower bound of the codeword size -depending on the word and number of errors: $m \geq 2t+n+1$. +That being said, we know the lower bound of the codeword size +depending on the word and number of errors: $m > 2t + \rho\cdot m + 1$. \begin{tcolorbox}[title=Berlekamp-Welch decoding, breakable, @@ -341,33 +342,36 @@ \subsection{Reed-Solomon code} colback=blue!20!white, colupper=blue!75!gray} ] - So, $G(a_i) = b_i$ for at least $\hat{t}$ of $m$ pairs in $\mathcal{D}$. We also know that - $\deg G(x) = n-1$ and $n < t < m$. It means that we already can recover polynomial - but we firstly have to deal with an errors. + \textbf{Assume:} $G(x_j) = y_j$ for at least $\hat{t}$ of $m$ pairs in + $\mathcal{D}$ with $G \in \mathbb{F}^{(\leq \rho\cdot m)}[x]$. - \tcbsubtitle{$\mathsf{Dec}(\mathcal{D})$} + \textit{Comment:} Since $t/m < 1-\rho$, recovering polynomial $G$ is + possible, but we need to somehow know which points are corrupted. + + \tcbsubtitle{$\mathsf{Dec}(\mathsf{RS}[\mathbb{F},\Omega,\rho], \{x_j, + y_j\}_{j \in [m]})$} \begin{itemize}[label=\ding{51}] \item Let's put an \textbf{error polynomial} $E(X)$ a polynomial which has roots at the error points. - \item So, $\deg E(x) = t < \frac{m}{2} - \frac{n}{2}$. + \item So, $\deg E(x) = t < \frac{m}{2} - \frac{\rho\cdot m + 1}{2}$. \item Our algorithm is based on the following equation: - $$C(a_i) = G(a_i)\cdot E(a_i), i \in 0..m-1$$ + $$C(x_j) = G(x_j)\cdot E(x_j), j \in [m]$$ where $C(x)$ is an arbitrary polynomial that we are going to find. - \item Note, that $\deg C(x) = \deg G(x) + \deg E(x) = n - 1 + t$ - \item Then, by solving the equation $C(a_i) = b_i\cdot E(a_i), i \in 0..m-1$ + \item Note, that $\deg C(x) = \deg G(x) + \deg E(x) = \rho\cdot m + t$ + \item Then, by solving the equation $C(x_j) = y_j\cdot E(x_j), j \in [m]$ we can find $C(x)$ and $E(x)$. \item This can be considered as a set of $m$ linear equations where we have - unknown $n+t$ coefficients + unknown $\rho \cdot m + t + 1$ coefficients of $C(x)$ and $t+1$ coefficient of $E(x)$, so it can be solved via linear - algebra while $m \geq 2t+n+1$. + algebra while $m > 2t + \rho\cdot m + 1$. \item Finally, we put $G(x) = \frac{C(x)}{E(x)}$ \end{itemize} \end{tcolorbox} The last equation follows from the observation that polynomial $C(x) - G(x)\cdot E(x)$ is zero in $\hat{t}$ points -while it has degree $n + t - 1$. It is easy to prove that the polynomial degree +while it has degree $\rho\cdot m + t$. It is easy to prove that the polynomial degree less then the number of it's roots, so $C(x) - G(x)\cdot E(x)$ is zero for each $x$. That is why from $C(x) = E(x)G(x)$ follows that $C(x)$ is divisible on $E(x)$. @@ -380,8 +384,8 @@ \subsubsection{Reed-Solomon(255,223) implementation} DVDs, and deep-space communication. It operates over $\mathbb{F}_{256}$ (the finite field of $256$ elements) and encodes data as polynomials evaluated at different field elements. Following our theorem, the number of errors -is constrained as $t < \frac{m}{2} - \frac{n}{2}$ which means that $t < 16$. -For our implementation we take $t = 15$. +is constrained as $t < \frac{m}{2} - \frac{\rho \cdot m + 1}{2} = \frac{m}{2} - \frac{n}{2}$ +which means that $t < 16$. For our implementation we take $t = 15$. Let's start from the basic parameters definition: \begin{lstlisting}[language=Python,numbers=none] From ca11ff65c1293b61ffc9de437aec89ccb2a24125 Mon Sep 17 00:00:00 2001 From: olegfomenko Date: Tue, 11 Mar 2025 15:26:34 +0200 Subject: [PATCH 7/9] fixed RS --- lectures/14-ecc.tex | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index 504a667..2899a2c 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -273,17 +273,16 @@ \subsection{Reed-Solomon code} \textbf{Reed-Solomon Code} is a function $\mathsf{RS}: \mathbb{F}^n \rightarrow \mathbb{F}^m$ that for each $n-1$ degree polynomial $A(x) \in \mathbb{F}[x]$ outputs its evaluation over $m$ points $f_0,...,f_{m-1} \in \mathbb{F}$. -We can show the equality to the original definition if we put $\rho = \frac{n - 1}{m}$ and $\Omega = \mathbb{F}^m$: +We can show the equality to the original definition if we put $\rho = \frac{n - 1}{m}$ and $\Omega \in \mathbb{F}^m$: \begin{equation*} - \mathsf{RS}\Big[\mathbb{F},\Omega = \mathbb{F}^m,\rho = \frac{n - 1}{m}\Big] = \left\{A(x)\big|_{x \in \Omega }: A \in \mathbb{F}^{(\leq n -1)}[x]\right\}, + \mathsf{RS}\Big[\mathbb{F},\Omega \in \mathbb{F}^m,\rho = \frac{n - 1}{m}\Big] = \left\{A(x)\big|_{x \in \Omega }: A \in \mathbb{F}^{(\leq n -1)}[x]\right\}, \end{equation*} \end{definition} \begin{remark} For the future definitions and theorems we will use the original definition. However, a simplified -nonation can be obtained by replacing the -$\Omega = F^m$, $|\Omega| = m$, and $\rho |\Omega| = n - 1$. +nonation can be obtained by replacing the $|\Omega| = m$, and $\rho |\Omega| = n - 1$. \end{remark} Let us now estimate some parameters of the Reed-Solomon code. From 1a53c3338ecdd1ba61f23a0ec000096bbb604574 Mon Sep 17 00:00:00 2001 From: ZamDimon Date: Tue, 11 Mar 2025 14:00:07 +0200 Subject: [PATCH 8/9] :adhesive_bandage: some fixes, forgot which ones --- lectures/14-ecc.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index 2899a2c..38285f1 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -70,12 +70,12 @@ \subsection{Preliminaries} Therefore, each block ECC that takes an input word of size $n$ and outputs a codeword of size $m>n$ can theoretically deal with $t=m-n$ errors. A block ECC also utilizes a relation between its distance and its theoretical correcting -capability: $t < \lfloor \frac{d-1}{2} \rfloor$. This relation exists because +capability: $t < \left\lfloor \frac{\delta-1}{2} \right\rfloor$. This relation exists because two arbitrary codewords can be decoded if they contain at most $t$ errors. Therefore, the distance between these codewords must be at least $2t$ to enable unique decoding (if it is less then two different codewords with errors can be represented as the same message, so the pre-image cannot be found correctly). -While the ECC distance is $d$, it leads to the relation above (check Figure +While the ECC distance is $\delta$, it leads to the relation above (check Figure \ref{fig:distance}). \begin{figure}[H] \centering From 1f756652987d44ba2b05abe94519a7dc063b0dd1 Mon Sep 17 00:00:00 2001 From: ZamDimon Date: Tue, 11 Mar 2025 15:34:37 +0200 Subject: [PATCH 9/9] :adhesive_bandage: fix ecc --- lectures/14-ecc.tex | 282 ++++++++++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 126 deletions(-) diff --git a/lectures/14-ecc.tex b/lectures/14-ecc.tex index 38285f1..10ccdc2 100644 --- a/lectures/14-ecc.tex +++ b/lectures/14-ecc.tex @@ -48,35 +48,37 @@ \subsection{Preliminaries} \textbf{message}, while the image of such function $\mathsf{im}(E)$ is what we call a \textbf{codeword}. Let us define that more formally. \begin{definition} -For each $\delta \in [0, 1]$ we call a function $E: \{0, 1\}^n \rightarrow \{0, -1\}^m$ an \textbf{error-correcting code} with distance $\delta$ if for each two -\emph{different} input strings $x \neq y \in \{0, 1\}^n$ the distance between -their images is more or equal $\delta$: that is, $\Delta(E(x), E(y)) \geq -\delta$. We call the image of the function $E$ +For each $\delta \in [0, 1]$ we call the image of the encoding function +$\mathsf{Enc}: \{0, 1\}^n \rightarrow \{0, 1\}^m$ an \textbf{error-correcting +code} with distance $\delta$ if for each two \emph{different} input strings $x +\neq y \in \{0, 1\}^n$ the distance between their images is more or equal to +$\delta$: that is, $\Delta(\mathsf{Enc}(x), \mathsf{Enc}(y)) \geq \delta$. In +other words, the code $\mathcal{C} \subseteq \{0,1\}^m$ is the following space: \begin{equation*} - \mathsf{im}(E) \equiv \{y \in \{0,1\}^m \; \text{such that} \; y=E(x) \; \text{for some} \; x \in \{0,1\}^n \} + \mathcal{C} = \mathsf{im}(\mathsf{Enc}) \equiv \{c \in \{0,1\}^m \; \text{such that} \; c=\mathsf{Enc}(x) \; \text{for some} \; x \in \{0,1\}^n \} \end{equation*} -the set of \textbf{codewords} of the corresponding code. + +To denote such a code, we use notation $[m,n,\delta]$. \end{definition} \begin{remark} In fact, the distance $\delta$ in the definition above is a lower bound of the distance between the codewords. This can be formally written as: \begin{equation*} - \delta = \min_{x \neq y \in \mathsf{im}(E)} \Delta(x, y) + \delta = \min_{x \neq y \in \mathsf{im}(\mathsf{Enc})} \Delta(x, y) \end{equation*} \end{remark} Therefore, each block ECC that takes an input word of size $n$ and outputs a codeword of size $m>n$ can theoretically deal with $t=m-n$ errors. A block ECC also utilizes a relation between its distance and its theoretical correcting -capability: $t < \left\lfloor \frac{\delta-1}{2} \right\rfloor$. This relation exists because -two arbitrary codewords can be decoded if they contain at most $t$ errors. -Therefore, the distance between these codewords must be at least $2t$ to enable -unique decoding (if it is less then two different codewords with errors can be -represented as the same message, so the pre-image cannot be found correctly). -While the ECC distance is $\delta$, it leads to the relation above (check Figure -\ref{fig:distance}). +capability: $t < \left\lfloor \frac{\delta-1}{2} \right\rfloor$. This relation +exists because two arbitrary codewords can be decoded if they contain at most +$t$ errors. Therefore, the distance between these codewords must be at least +$2t$ to enable unique decoding (if it is less then two different codewords with +errors can be represented as the same message, so the pre-image cannot be found +correctly). While the ECC distance is $\delta$, it leads to the relation above +(check \Cref{fig:distance}). \begin{figure}[H] \centering \includegraphics[width=0.5\linewidth]{images/lecture_14/circles.png} @@ -84,7 +86,13 @@ \subsection{Preliminaries} \label{fig:distance} \end{figure} -\subsection{Walsh-Hadamard code} +Error-Correcting Codes are extensively used in zk-STARKs. In particular, +we would need to define the so-called \textit{Reed-Solomon Codes}. But +before delving into them, we start from the fundamental construction --- +\textit{Walsh-Hadamard Code}, and consider the properties of ECC based +on it. + +\subsection{Walsh-Hadamard Code} The Hadamard code, named after French mathematician Jacques Hadamard, is an error-correcting block code designed for detecting and correcting errors in message transmissions over highly noisy or unreliable channels. In 1971, NASA @@ -101,15 +109,19 @@ \subsection{Walsh-Hadamard code} \begin{equation*} \langle x, y \rangle = \sum_{i \in [n]} x_iy_i \; \text{over} \; \mathbb{F}_2 \end{equation*} + +Equivalently, $\langle x, y \rangle = \bigoplus_{i \in [n]}(x_i \wedge y_i)$ in boolean arithmetic. + \end{definition} Now, we are ready to define the Walsh-Hadamard code. \begin{definition}[Walsh-Hadamard code] -The \textbf{Walsh-Hadamard code} is a function $\mathsf{Had}: \{0, 1\}^n -\rightarrow \{0, 1\}^{2^n}$ that maps each $x \in \{0,1\}^n$ into the string $z -\in \{0, 1\}^{2^n}$ of size $2^n$ where $z_y = \langle x, y\rangle$ for every $y -\in \{0, 1\}^n$. More concisely, this is written as: +The \textbf{Walsh-Hadamard Code} is defined over an encoder function +$\mathsf{Had}: \{0, 1\}^n \rightarrow \{0, 1\}^{2^n}$ that maps each $x \in +\{0,1\}^n$ into the string $z \in \{0, 1\}^{2^n}$ of size $2^n$ where $z_y = +\langle x, y\rangle$ for every $y \in \{0, 1\}^n$. More concisely, this is +written as: \begin{equation*} \mathsf{Had}(x) = \left(\langle x, y \rangle\right)_{y \in \{0, 1\}^n} \end{equation*} @@ -136,17 +148,27 @@ \subsection{Walsh-Hadamard code} 0 \cdot 1 + 1 \cdot 1 \\ \end{bmatrix} = (0, 1, 0, 1) \end{gather*} -\textbf{Decoding.} To decode a received codeword, we use the following -technique: for each possible input word $x \in \{0, 1\}^n$, we calculate $C(x,y) -= (-1)^{\langle x, y \rangle}$ for each $y \in \{0, 1\}^n$. We also represent -our received codeword $\widetilde{y}$ as $\{(-1)^{\widetilde{y}_i}\}_{i \in -[2^n]}$. For each possible word $\widetilde{x} \in \{0, 1\}^n$ we calculate the -sum $S(u) = \sum_{s \in [2^n]} (-1)^{\widetilde{y}_i} \cdot C(\widetilde{x},s)$. -This sum represents the similarity between the word $\widetilde{x}$ and the -encoded word. If both words have the same sign at position $s$, the sum -increases; otherwise, it decreases. The decoded word is the one with the highest -sum. \end{example} + +Now, we consider the decoding procedure of the Walsh-Hadamard ECC. + +\textbf{Decoding.} Suppose we received the codeword $c \in \{0,1\}^{2^n}$ and we +need to decode it to receive the corresponding word $\hat{x} \in \{0,1\}^n$ such +that $c=\mathsf{Enc}(\hat{x})$. + +We use the following technique: for each possible input words $x \in \{0,1\}^n$ +and word $y \in \{0, 1\}^n$, we calculate $\sigma(x,y) = (-1)^{\langle x, y +\rangle}$ (can be done in advance). We represent our received codeword $c$ as a +set of $2^n$ elements $\{(-1)^{c_i}\}_{i \in [2^n]}$. For each possible word $x +\in \{0, 1\}^n$ we calculate the sum +\begin{equation*} + S(x) = \sum_{y \in [2^n]} (-1)^{c_i}\sigma(x, y) +\end{equation*} +This sum represents the similarity between the word $x$ and the encoded word. If +both words have the same sign at position $s$, the sum increases; otherwise, it +decreases. The decoded word $\hat{x}$ is the word $x \in \{0,1\}^n$ with the +highest sum: that is, $\hat{x} \gets \arg\max_{x \in \{0,1\}^n} S(x)$. + To define the distance for the Walsh-Hadamard ECC we first need to prove an additional lemma: \begin{lemma}[Random subsum principle] The following statement holds: @@ -160,14 +182,14 @@ \subsection{Walsh-Hadamard code} \end{lemma} \begin{proof} -Note, that $\langle u, x \rangle \neq \langle v, x \rangle$ holds if and only if -$1 = \langle u, x \rangle + \langle v, x \rangle = \langle (u + v), x \rangle$, -where $+$ is a binary addition over $\mathbb{F}_2$. Then, we can rewrite this as -$\langle (u + v), x \rangle = \sum_{(u + v)_i \neq 0} x_i$. Since $u + v \neq -0^n$ from the initial assumption and $x$ is a uniformly random string, the -$\sum_{(u + v)_i \neq 0} x_i = 1$ holds with probability $\frac{1}{2}$. So, -finally, we conclude that $\langle u, x \rangle \neq \langle v, x \rangle$ with -probability $\frac{1}{2}$. +Note, that $\langle u, x \rangle \neq \langle v, x \rangle$ iff $\langle u, x +\rangle + \langle v, x \rangle = 1$, or, equivalently, $\langle w, x \rangle = +1$ for $w := u+v$ (inner product is linear). We then rewrite this as $\langle w, +x \rangle = \sum_{i \in [n]: w_i \neq 0} x_i = 1$. Since $u \neq v$ from the +initial assumption, $w = u + v$ is not a zero string. Therefore, since $x$ is a +uniformly random string, the equality $\sum_{i \in [n]: w_i \neq 0} x_i = 1$ +holds with probability $\frac{1}{2}$. So, finally, we conclude that $\langle u, +x \rangle \neq \langle v, x \rangle$ with probability $\frac{1}{2}$. \end{proof} \begin{lemma} The Walsh-Hadamard code $\mathsf{Had}$ is an error-correcting code with distance $\delta = \frac{1}{2}$. @@ -191,18 +213,22 @@ \subsection{Reed-Solomon code} communications, DVB, and ATSC, as well as storage solutions such as RAID6. Essentially, the difference between the variants Reed-Solomon code lies in the assumptions on the basis of which their encoding and decoding algorithms are determined. -Firstly, let's start from the definition of the input data that differs from binary: +Firstly, let us start from the definition of the input data that differs from +binary: \begin{definition} For some alphabet $\Sigma$ and elements $x,y \in \Sigma^n$, we define the relative distance $\Delta(x, y) = \frac{1}{n}\left|\{i \in [n]: x_i \neq y_i\}\right|$. \end{definition} -Now we can describe error-correcting codes for the alphabets that differ from binary. Also, let's describe one -subclass of the block ECCs to which the Reed-Solomon code belong: +Now we can describe error-correcting codes for the alphabets that differ from +binary. Also, let's describe one subclass of the block ECCs to which the +Reed-Solomon code belong to. \begin{definition}[Linear code] -Linear code is a code where the set of codewords forms a linear space: +Linear code is a code where the set of codewords forms a linear space $\mathcal{C}$ over +field $\mathbb{F}$: \begin{itemize} - \item[--] For each two codewords $c_1, c_2$, the $c_1 + c_2$ is also a codeword. - \item[--] For the codeword $c$ and constant $\alpha$ the $\alpha c$ is also a codeword. + \item[--] For each two codewords $c_1, c_2 \in \mathcal{C}$, the $c_1 + c_2 \in \mathcal{C}$ is also a codeword. + \item[--] For the codeword $c \in \mathcal{C}$ and constant $\lambda \in + \mathbb{F}$ the $\lambda c \in \mathcal{C}$ is a codeword. \end{itemize} \end{definition} @@ -233,25 +259,29 @@ \subsection{Reed-Solomon code} Now, we shift our focus towards one of the core zk-STARK components: the Reed-Solomon code. This code is a linear error-correcting code that operates -over the certain finite field $\mathbb{F}$ and polynomials $\mathbb{F}[X]$. The -definition is following: +over the certain finite field $\mathbb{F}$ and polynomials $\mathbb{F}[X]$. Note +that one might encounter different definitions of the Reed-Solomon codes. We +first present the definition used in zk-STARKs and then provide a simplified, +more traditional definition. -\begin{definition}[Reed-Solomon code] +\begin{definition}[Reed-Solomon Code] Let $\Omega \subseteq \mathbb{F}$ be some \emph{evaluation domain} with a rate -parameter $\rho \in (0,1]$. Formally, the \textbf{Reed-Solomon Code} -$\mathsf{RS}[\mathbb{F},\Omega,\rho]$ is defined as the space of functions $f: -\Omega \to \mathbb{F}$ that are evaluations of polynomials of degree up to -$\rho|\Omega|$. Put much more simply, the codeword of the Reed-Solomon code is an -evaluation of a polynomial of degree less than $\rho|\Omega|$ over the points of the -evaluation domain $\Omega$: +parameter $\rho \in (0,1]$ of size $m := |\Omega|$. Formally, the +\textbf{Reed-Solomon Code} $\mathsf{RS}[\mathbb{F},\Omega,\rho]$ is defined as +the space of functions $f: \Omega \to \mathbb{F}$ that are evaluations of +polynomials of degree up to $\rho m$. Put much more simply, the codeword +of the Reed-Solomon code is an evaluation of a polynomial of degree less than +$\rho m$ over the points of the evaluation domain $\Omega$: \begin{equation*} - \mathsf{RS}[\mathbb{F},\Omega,\rho] = \left\{f(x)\big|_{x \in \Omega}: f \in \mathbb{F}^{(\leq \rho |\Omega|)}[x]\right\}, + \mathsf{RS}[\mathbb{F},\Omega,\rho] = \left\{f(x)\big|_{x \in \Omega}: f \in \mathbb{F}^{(\leq \rho m)}[x]\right\}, \end{equation*} where we abuse the notation to denote $f(x)\big|_{x \in \Omega} = \left(f(\omega)\right)_{\omega \in \Omega}$. \end{definition} -The definition is very hard to grasp on its own, so we help you with -an example. + +The definition is very hard to grasp from the very first glance, so we help you +with the following example. + \begin{example} Suppose $\mathbb{F} = \mathbb{F}_7$ and let evaluation domain be $\Omega = \{1,2,5,6\}$. Then, the Reed-Solomon code $\mathsf{RS}[\mathbb{F}_7, \Omega, @@ -263,73 +293,74 @@ \subsection{Reed-Solomon code} \end{example} \begin{remark} - Typically, we assume that $\rho|\Omega|$ is an integer, so we omit rounding + Typically, we assume that $\rho|\Omega|=\rho m$ is an integer, so we omit rounding operations in the following discussion. \end{remark} -Additionally, the presented original definition can be simplified as follows: -\begin{definition}[Reed-Solomon code] -For a field $\mathbb{F}$ and numbers $0 < n \leq m < |\mathbb{F}|$ the -\textbf{Reed-Solomon Code} is a function $\mathsf{RS}: \mathbb{F}^n \rightarrow \mathbb{F}^m$ that for each -$n-1$ degree polynomial $A(x) \in \mathbb{F}[x]$ outputs its evaluation over $m$ points $f_0,...,f_{m-1} \in \mathbb{F}$. - -We can show the equality to the original definition if we put $\rho = \frac{n - 1}{m}$ and $\Omega \in \mathbb{F}^m$: +As mentioned earlier, the presented definition can be simplified to the more +traditional one as follows: +\begin{definition}[Reed-Solomon Code (simplified)] +For a field $\mathbb{F}$ and numbers $n \leq m < |\mathbb{F}|$, the +\textbf{Reed-Solomon Code} is a code over the encoding function +$\mathsf{Enc}_{\text{RS}}: \mathbb{F}^n \rightarrow \mathbb{F}^m$ that for each +$n$ degree polynomial $p \in \mathbb{F}[x]$ outputs its evaluation over $m$ +points $x_0,\ldots,x_{m-1} \in \mathbb{F}$. +\end{definition} +We can show the equality to the original definition if we put $\rho := +\frac{n}{m}$ (note that here $\rho$ has an evident meaning: $\rho$ shows how +many times the evaluation domain is larger than the word space) and $\Omega := +(x_0,\dots,x_{m-1}) \subset \mathbb{F}$: \begin{equation*} - \mathsf{RS}\Big[\mathbb{F},\Omega \in \mathbb{F}^m,\rho = \frac{n - 1}{m}\Big] = \left\{A(x)\big|_{x \in \Omega }: A \in \mathbb{F}^{(\leq n -1)}[x]\right\}, + \mathsf{RS}\left[\mathbb{F},\Omega\,\textcolor{gray}{\left(=\hspace{-2px}\mathbb{F}^m\right)}, \rho\,\textcolor{gray}{\left(=\hspace{-2px}\frac{n}{m}\right)}\right] = \left\{(p(x_0),\dots,p(x_{m-1})): p \in \mathbb{F}^{(\leq n)}[x]\right\}, \end{equation*} -\end{definition} \begin{remark} -For the future definitions and theorems we will use the original definition. However, a simplified -nonation can be obtained by replacing the $|\Omega| = m$, and $\rho |\Omega| = n - 1$. +For the future definitions and theorems we will stick to the original +definition. However, keep in mind that a simplified notation can be obtained by +replacing $\Omega = (x_i)_{i \in [m]}$, $|\Omega| = m$, and $\rho |\Omega| = n$. \end{remark} Let us now estimate some parameters of the Reed-Solomon code. \begin{lemma} -The Reed-Solomon code has a distance of $1 - \frac{n-1}{m}$. +The Reed-Solomon code has a distance of $1 - \rho$. \end{lemma} \begin{proof} -Let $m = |\Omega|$. The word is a polynomial of degree up to $\rho \cdot m$ so it can have no more then -$\rho \cdot m$ roots. The codeword is the evaluation of this polynomial at $m > \rho \cdot m$ distinct points, -meaning it must contain at least $m-\rho \cdot m =(1-\rho)m$ non-zero values (since at most -$\rho \cdot m$ points can be the roots of our word polynomial). Since the Reed-Solomon code is a -linear code, we can use its property to define its distance as -$\frac{(1-\rho)m}{m} = 1-\rho$. +The word is a polynomial of degree up to $\rho m$ so it can have no more then +$\rho m$ roots. The codeword is the evaluation of this polynomial at $m > \rho +m$ distinct points, meaning it must contain at least $m-\rho m =(1-\rho)m$ +non-zero values (since at most $\rho m$ points can be the roots of our word +polynomial). Since the Reed-Solomon code is a linear code, we can use its +property to define its distance as $\frac{(1-\rho)m}{m} = 1-\rho$. \end{proof} -By following the ``Unisolvence theorem'' we know that to recover $\rho \cdot m$ degree polynomial -we need at least $\rho \cdot m + 1$ points for interpolation. Because the Reed-Solomon code -performs polynomial evaluation over $m > \rho \cdot m$ points we can still interpolate (or decode) -the polynomial even if some points are corrupted during data transmission. +\textbf{Decoding.} By following the ``Unisolvence theorem'' we know that to +recover $\rho m$ degree polynomial we need at least $\rho m + 1$ points for +interpolation. Because the Reed-Solomon code performs polynomial evaluation over +$m > \rho m$ points we can still interpolate (or decode) the polynomial even if +some points are corrupted during data transmission. \begin{theorem} -Suppose for a given list of pairs $\mathcal{D} = \{(x_i, y_i)\}_{i \in [m]}$ of elements -in $\mathbb{F}$ there exists a unique polynomial $G(x) \in \mathbb{F}^{(\leq \rho\cdot m)}[x]$ such that -$G(x_j) = y_j$ for at least $\hat{t} > \frac{m}{2} + \frac{\rho\cdot m + 1}{2}$ of them. Then, there exists a -\textit{polynomial-time algorithm} $\mathsf{Dec}$ (in $O(N^{\gamma})$ for some -constant $\gamma>1$), extracting the polynomial: $\mathsf{Dec}(\mathcal{D}) = -G$. +Suppose for a given list of pairs $\mathcal{D} = \{(x_i, y_i)\}_{i \in [m]}$ of +elements in $\mathbb{F}$ there exists a unique polynomial $f \in +\mathbb{F}^{(\leq \rho m)}[x]$ such that $f(x_j) = y_j$ for at least $T := +\frac{1+\rho}{2}m$ of them. Then, there exists a \textit{polynomial-time +algorithm} $\mathsf{Dec}$ (in $O(N^{\gamma})$ for some constant $\gamma>1$), +extracting this very polynomial: $\mathsf{Dec}(\mathcal{D}) = f$. \end{theorem} -\begin{proof} -The assumption that at least $\hat{t} > \frac{m}{2} + \frac{\rho\cdot m + 1}{2}$ points must be -``interpolatable'' for some $G(x) \in \mathbb{F}^{(\leq \rho\cdot m)}[x]$ can be transformed into the -corresponding constraint on the number of possible errors $t$: -\begin{equation*} - t = m - \hat{t} < m - \frac{m}{2} - \frac{\rho\cdot m + 1}{2} = \frac{m}{2} - \frac{\rho\cdot m + 1}{2} -\end{equation*} -Alternatively, this defines a necessary constraint that will be useful later: -\begin{gather*} - t < \frac{m}{2} - \frac{\rho\cdot m + 1}{2}\\ - 2t < m - \rho\cdot m + 1\\ - m > 2t + \rho\cdot m + 1 -\end{gather*} -That being said, we know the lower bound of the codeword size -depending on the word and number of errors: $m > 2t + \rho\cdot m + 1$. +\textbf{Note.} The assumption that at least $T > \frac{1+\rho}{2}m$ points must +be ``interpolatable'' for some $f \in \mathbb{F}^{(\leq \rho m)}[x]$ implies +that $t < \frac{1}{2}\delta m$ for distance $\delta$. Indeed, $t = m - T$ and +since $T>\frac{1+\rho}{2}m$, we obtain the condition $t < m - \frac{1+\rho}{2}m += \frac{1-\rho}{2}m = \frac{1}{2}\delta m$. + +\begin{proof} Let us construct the decoding algorithm $\mathsf{Dec}$, which is + called the \textit{Berlekamp-Welch decoding}. -\begin{tcolorbox}[title=Berlekamp-Welch decoding, +\newpage +\begin{tcolorbox}[title=Berlekamp-Welch Decoding, breakable, colback=blue!5!white, colframe=blue!75!black, @@ -341,39 +372,38 @@ \subsection{Reed-Solomon code} colback=blue!20!white, colupper=blue!75!gray} ] - \textbf{Assume:} $G(x_j) = y_j$ for at least $\hat{t}$ of $m$ pairs in - $\mathcal{D}$ with $G \in \mathbb{F}^{(\leq \rho\cdot m)}[x]$. - - \textit{Comment:} Since $t/m < 1-\rho$, recovering polynomial $G$ is - possible, but we need to somehow know which points are corrupted. + \textbf{Assume:} $f(x_j) = y_j$ for at least $T$ of $m$ pairs in + $\mathcal{D}$ with $f \in \mathbb{F}^{(\leq \rho m)}[x]$. \tcbsubtitle{$\mathsf{Dec}(\mathsf{RS}[\mathbb{F},\Omega,\rho], \{x_j, y_j\}_{j \in [m]})$} \begin{itemize}[label=\ding{51}] - \item Let's put an \textbf{error polynomial} $E(X)$ a polynomial which has - roots at the error points. - \item So, $\deg E(x) = t < \frac{m}{2} - \frac{\rho\cdot m + 1}{2}$. + \item Introduce the \textbf{error polynomial} $e \in \mathbb{F}^{(\leq + \delta m/2)}[x]$ which has roots at the error points. Notice that the + degree of the error polynomial is less than $\delta m/2$, which follows + from the estimate on $t$. \item Our algorithm is based on the following equation: - $$C(x_j) = G(x_j)\cdot E(x_j), j \in [m]$$ - where $C(x)$ is an arbitrary polynomial that we are going to find. - \item Note, that $\deg C(x) = \deg G(x) + \deg E(x) = \rho\cdot m + t$ - \item Then, by solving the equation $C(x_j) = y_j\cdot E(x_j), j \in [m]$ - we can find $C(x)$ and $E(x)$. - \item This can be considered as a set of $m$ linear equations where we have - unknown $\rho \cdot m + t + 1$ coefficients - of $C(x)$ and $t+1$ coefficient of $E(x)$, so it can be solved via linear - algebra while $m > 2t + \rho\cdot m + 1$. - \item Finally, we put $G(x) = \frac{C(x)}{E(x)}$ + \begin{equation*} + h(x_i) = f(x_i)e(x_i), \; \text{for every} \; i \in [m]. + \end{equation*} + Here, $h(x) \in \mathbb{F}^{\left(\leq \frac{1+\rho}{2}m\right)}$ is an + arbitrary polynomial that we will try to find. Note, that its degree + $\deg h = \deg f + \deg e \leq \frac{1+\rho}{2}m$. + \item Since $f(x_i)=y_i$, the equation translates to $f(x_i)=y_ie(x_i)$ + for each $i \in [m]$, where coefficients of $f$ and $e$ are to be found. + Notice that this is a linear equation over coefficients of $f$ and $e$ + with $\frac{1+\rho}{2}m$ unknowns and $m$ equations. Since $m>\frac{1+\rho}{2}m$ + for any $m$, we are guaranteed to find a solution. + \item Finally, we set $f \gets h/e$. \end{itemize} - \end{tcolorbox} -The last equation follows from the observation that polynomial -$C(x) - G(x)\cdot E(x)$ is zero in $\hat{t}$ points -while it has degree $\rho\cdot m + t$. It is easy to prove that the polynomial degree -less then the number of it's roots, so $C(x) - G(x)\cdot E(x)$ is zero for -each $x$. That is why from $C(x) = E(x)G(x)$ follows that $C(x)$ is divisible -on $E(x)$. + +It might be unclear why $e \mid h$ (so that $f$ is not a rational function). +Note that $\mu(x) := f(x)-h(x)e(x)$ is zero in $m$ points and has a degree less +than $m$ (namely, $\rho m$). Therefore, if $\mu$ has $m$ roots and has a degree +$\deg \mu < m$, it must be identically zero. This means that $f(x) = h(x)/e(x)$ +over the whole $\Omega$. \end{proof} \subsubsection{Reed-Solomon(255,223) implementation}