{"slug": "ckks-polynomials-the-canonical-embedding-and-encoding", "title": "CKKS — Polynomials, the Canonical Embedding, and Encoding", "summary": "CKKS homomorphic encryption scheme, beginning with the mathematical background needed to understand it, including the polynomial ring and the canonical embedding used for encoding. It explains that CKKS, first introduced in 2016, enables approximate arithmetic on real and complex numbers, making it suitable for applications like neural network inference. The article also covers key improvements to CKKS, such as bootstrapping for full homomorphism and the residue number system variant for more efficient computation.", "body_md": "In this tutorial series, I will introduce the CKKS homomorphic encryption\nscheme from the ground up, in rather intricate detail. Each article in this\nseries corresponds to a pull request on [a GitHub\nrepository](https://github.com/j2kun/ckks-tutorial). The code for this article\nis in [this pull request](https://github.com/j2kun/ckks-tutorial/pull/2).\nFollow along by cloning the\nrepository and checking out the code at the relevant commit.\n\nThis first article will cover some of the mathematical background necessary in the formulation of the CKKS encryption scheme, specifically the polynomial ring used in the most basic version of CKKS, and the canonical embedding used to encode cleartext messages as plaintexts.\n\nThis series will contain plenty of mathematics, but I may abbreviate some\nverbose definitions, especially those that I would expect to be familiar to\nreaders of this blog (such as the formal definition of a ring). In other words,\nI’ll assume basic undergraduate mathematics familiarity, with some reminders. A\ngood accompaniment for this series would be [The Beginner’s Textbook for Fully\nHomomorphic Encryption](https://fhetextbook.github.io/) by Ronny Ko, which\ncomplements this series in giving more complete (albeit terse) definitions,\nformulas, and proofs.\n\n## A brief history of CKKS\n\nSome of the terms used in this section may make more sense if you’ve read\nmy [high-level technical overview of homomorphic encryption](/2024/05/04/fhe-overview/).\nWe will re-cover all of this in detail in future articles.\n\nThe original CKKS homomorphic encryption scheme was introduced in the 2016\npaper [Homomorphic Encryption for Arithmetic of Approximate\nNumbers](https://eprint.iacr.org/2016/421) by Jung Hee Cheon, Andrey Kim, Miran\nKim, and Yongsoo Song as a joint collaboration between Seoul National\nUniversity and UC San Diego. 1 Its primary innovation was to handle\napproximate arithmetic on real or complex numbers, rather than prior schemes\nwhich only handled exact arithmetic on integers. This is relevant in contexts\nlike neural network inference, where the calculations can be inexact and still\nuseful. In particular, CKKS allows the inexactness of fixed-point arithmetic\nto coexist with the error introduced by the homomorphic encryption scheme itself.\n\nAfter its initial publication, several followup papers made improvements to\nCKKS that elevated it to the state of the art. First, and most importantly, a\nbootstrapping procedure [was found in 2018](https://eprint.iacr.org/2018/153)\nthat made CKKS “fully homomorphic.” Subsequent years saw a plethora of additional\nimprovements and variants to CKKS bootstrapping. Experts would even say\nthere are too many variants to keep track of.\n\nThe second major improvement was the introduction of the [residue number system\nvariant of CKKS](https://eprint.iacr.org/2018/931) in 2017. The original CKKS\nscheme used large integer arithmetic, in particular doing arithmetic modulo\nhundred-bit or even thousand-bit moduli. Using a residue number system (RNS)\nallows one to replace the (inherently serial) carry propagation required for\nlarge-precision arithmetic with parallel operations on vectors of 64-bit\nvalues.[2](#fn:2)\n\nCombining RNS with bootstrapping produces what, in my view, is the “baseline” version of CKKS that most new works extend or use for contrast.\n\n## Plaintexts and a polynomial ring\n\nThe main setting for CKKS is a particular polynomial ring. We start with some ring $R$ of coefficients for the polynomials $R[x]$; sometimes $R$ will be the integers, reals, but more often it will be the field of integers modulo a prime.\n\nCKKS is an encryption scheme, and in every encryption scheme there are three\ndistinct spaces: the *cleartext* space, the *plaintext* space, and the\n*ciphertext* space.\n\nCleartexts describe the atomic message units (e.g., a vector of 32-bit integers of a fixed size). The user must decide how to split their larger program data into cleartext units, say, by chunking. Plaintexts describe preprocessing required to make a cleartext compatible with the encryption scheme. And ciphertexts are the form of the messages after they are encrypted.\n\nI spell all this out because, while many encryption schemes don’t have major differences between cleartext and plaintext space, CKKS uses a sophisticated transformation. This article focuses purely on the conversion between the cleartext and plaintext space.\n\nFix a parameter $N$, a power of two, which will be used to define the polynomial ring.\nA CKKS cleartext is a vector of $N/2$ complex numbers. 3\nA CKKS plaintext is an element of the ring\n\nAs a reminder, the coefficients $\\mathbb{Z}/Q\\mathbb{Z}$ form the ring of integers with arithmetic done modulo $Q$. If $Q$ were a prime, this would form a field, but in most cases $Q$ is composite.\n\nAs a second reminder, the polynomial modulus converts the ring of polynomials mod $Q$ into a quotient ring where two polynomials $p(x)$ and $q(x)$ are equivalent if they have the same remainder when dividing by $x^N + 1$. Some features that are important for computation:\n\n- Elements of this ring have degree bounded by $N-1$ (when choosing their minimal-degree coset representative). So a polynomial can be viewed as a vector of $N$ entries, the coefficients at degrees 0 to $N-1$.\n- One can identify $x^N$ with $-1$, and this gives a baseline method to reduce larger polynomials to their canonical representative: take each coefficient at degree $k \\geq N$ and add it with a sign flip to the coefficient at degree $k - N$.\n- Because of the previous item, multiplying a polynomial in this ring by a\nmonomial can be implemented by\n*cyclically*rotating the coefficient vector and sign-flipping the values that wrap around an odd number of times. This operation is also known as*negacyclic rotation*.\n\nThere are other important structures of this ring for cryptographic\nreasons. 4 For one, the value of $N$ being a power of two\nensures this polynomial forms a\n\n*number field*. I don’t want to go too deeply into Galois theory here, but the basic idea of a number field is that you start from the rational numbers $\\mathbb{Q}$, pick a finite number of elements $\\alpha_1, \\dots, \\alpha_r$ not in $\\mathbb{Q}$ (in our case they will be complex roots of unity), and “add them” to $\\mathbb{Q}$, forming an\n\n*extension field*$\\mathbb{Q}(\\alpha_1, \\dots, \\alpha_r)$ by also including all the derived quantities required to satisfy the field axioms (inverses, sums and products, sums of products, etc.).\n\nIn order to be a number field, these elements need to have some finite algebraic formula that gets them back to zero. In other words, the degree of $\\mathbb{Q}(\\alpha_1, \\dots, \\alpha_r)$ as a $\\mathbb{Q}$-vector space must be finite. The simplest example is $\\mathbb{Q}(\\sqrt{2})$, which has degree 2 because $\\sqrt{2}$ is a root of the polynomial $x^2 - 2$.\n\n[5](#fn:5)Back to CKKS, the polynomial ring $(\\mathbb{Z}/Q\\mathbb{Z})[x] \\Big / (x^N+1)$ is not\nobviously a number field. You have to do a bit of work to first identify $\\mathbb{Z}[x] \\Big / (x^N+1)$\nwith $K = \\mathbb{Q}(\\omega_{2N})$, where $\\omega_{2N}$ is a primitive $2N$-th\nroot of unity. 6 Once you do, taking a quotient by $Q$ in the\ncoefficients translates to a quotient ring $K / QK$.\nAnother angle is to start from $\\mathbb{Z}[x] / (x^N + 1)$, identify that\nas the ring of integers of\nthe number field $\\mathbb{Q}[x] / (x^N + 1) = \\mathbb{Q}(\\omega_{2N})$,\nand take a quotient by the modulus $Q$.\n\nWe will touch on this more later in the series. The choice of $N$ and $Q$ implies a particular structure of this quotient ring, which impacts how we implement various homomorphic operations. In particular, it affects the efficiency of the number theoretic transform. But for this article, what matters is mainly that the plaintexts are polynomials and that their coefficients are discrete. This implies two obstacles:\n\n- We must transform a vector of complex numbers into a polynomial.\n- We must discretize an inherently continuous quantity, complex numbers.\n\n## The canonical embedding\n\nThe tool that CKKS uses to solve both of these problems is called\n*the canonical embedding*. This term has a lot of abstract definitions\nin different parts of mathematics, but for our purposes the canonical\nembedding has a simple definition.\n\n**Definition:** Let $N$ be a power of two, and let $p(x)$ be a polynomial\nin $\\mathbb{C}[x] / (x^N + 1)$. Then the *canonical embedding*\nof $p(x)$ in $\\mathbb{C}^N$ is the vector of evaluations of $p(x)$\nat the roots of $x^N + 1$. In particular, for a primitive $2N$-th root of unity\n$\\omega = e^{2 \\pi i / (2N)} = e^{\\pi i / N}$\n(which generates the roots of $x^N + 1$),\nthe canonical embedding of $p(x)$ is the vector\n\nDefine the *canonical embedding* $\\sigma_N$ to map polynomials to their\nevaluations at the odd powers of the complex $2N$-th roots of unity.[7](#fn:7)\n\nLet’s prove some properties of the canonical embedding.\n\n**Homomorphism:** Evaluating a polynomial at a fixed value is a homomorphism\nwith respect to addition and scaling of the polynomials ($(p+q)(x) = p(x) +\nq(x)$ by definition), and the same is true componentwise for different\nevaluations, so $\\sigma_N$ is a homomorphism from $\\mathbb{C}[x] / (x^N + 1)\n\\to \\mathbb{C}^N$.\n\n**Well-defined**: Let $p(x)$ and $q(x)$ have the property that $x^N + 1$ divides $p(x) - q(x)$.\nThen for any root $r$ of $x^N + 1$ we have $p(r) - q(r) = 0$. Hence,\n\n**Conjugate symmetry when coefficients are real:** when the input $p(x)$\nto the canonical embedding happens to have real coefficients,\nit holds that $p(x)$ commutes with complex conjugation of its inputs, i.e.,\n$p(\\overline{x}) = \\overline{p(x)}$. Combine this with the fact\nthat the roots of $x^N + 1$ come in conjugate pairs:\n\nAnd you get that, in this special case of real coefficients, the canonical embedding has a special conjugate symmetry: the second half of the vector’s entries are the reversed-complex conjugates of the first half.\n\n\\[ \\sigma_N(p) = ( p(\\omega^1), p(\\omega^3), \\dots, p(\\omega^{N-1}), \\overline{p(\\omega^{N-1})}, \\dots, \\overline{p(\\omega^3)}, \\overline{p(\\omega^1)} ) \\]This property has been named the “Hermitian” property, and given a name: $\\mathbb{H}^N$ is defined as the set of complex vectors in $\\mathbb{C}^N$ whose second half is the reversed-complex conjugates of the first half.\n\nYou might think that, because the second half of $\\mathbb{H}^N$ is uniquely determined by the first half, that $\\mathbb{H}^N$ is isomorphic to $\\mathbb{C}^{N/2}$. You’d be right, but you have to be careful. Because despite having complex-valued entries, $\\mathbb{H}^N$ is not a vector space over $\\mathbb{C}$ at all. Scalar multiplication by a complex number does not preserve the conjugate symmetry. It only does so if the scalar is real. So $\\mathbb{H}^N$ and $\\mathbb{C}^{N/2}$ are isomorphic, but only as $\\mathbb{R}$-vector spaces.\n\nThe above limitation is no problem, however,\nbecause we actually *want* our input vectors to be real-valued polynomials\n(so we can round them to integer-coefficient plaintexts).\nThis leads us to the next fact, which is the converse of the “Conjugate\nsymmetry when coefficients are real” fact above.\n\n**Proposition:** Let $v \\in \\mathbb{H}^N$,\nand $\\sigma_N : \\mathbb{C}[x] \\Big / (x^N + 1) \\to \\mathbb{C}^N$\nbe the canonical embedding.\nThen $\\sigma_N^{-1}(v) \\in \\mathbb{R}[x] \\Big / (x^N + 1)$,\ni.e., has real-valued coefficients.\n\nFinally, the last property, which I will not prove here (see Appendix C of\n[Damgård-Pastro-Smart-Zakarias](https://eprint.iacr.org/2011/535)),\nrelates the geometry of the input and output of the canonical embedding.\nThis is useful when analyzing the noise growth of CKKS ciphertexts.\nIn fact, as far as I can tell this was one of the core reasons\nthe original CKKS authors bothered with all this machinery:\n\n**Proposition:** Fix $N$ and let $\\sigma = \\sigma_N$ be the canonical\nembedding as defined above. Let $\\left \\| x \\right \\|$ denote the infinity-norm\nof $x$ (the magnitude of the largest component). Then for all $p(x), q(x)$,\n\nMoreover, there is a constant $c$ (depending only on $N$) such that for every $p(x)$,\n\n\\[ \\left \\| p \\right \\| \\leq c \\left \\| \\sigma(p) \\right \\| \\]These facts allow one to measure the growth of polynomial error in the CKKS scheme by analyzing the growth of the canonical embeddings. We will come back to that topic in future articles.\n\n## Implementing the canonical embedding and its inverse\n\nIn this section we’ll implement the canonical embedding and its inverse in\nPython. Reminder, the code can be found [in commit 386f028](https://github.com/j2kun/ckks-tutorial/pull/2/changes/386f028f079c91968a90846f80500321c60930b7)\nof [this pull request](https://github.com/j2kun/ckks-tutorial/pull/2) for the\noverall tutorial series.\n\nBecause the canonical embedding involves evaluating a polynomial\nat a set of complex roots of unity, we naturally turn to the\nFast Fourier Transform. See [“Polynomial Multiplication Using the FFT”](/2022/11/16/polynomial-multiplication-using-the-fft/)\nand [“Negacyclic Polynomial Multiplication](/2022/12/09/negacyclic-polynomial-multiplication/)\nfor more details of why this is a good approach.\nIn particular, the canonical embedding and its inverse\nreduce to particular invocations of `fft`\n\nand `ifft`\n\n.\n\nWe start with a simple `Polynomial`\n\nclass that wraps the coefficients\nand $N$.\n\n```\nclass Polynomial:\n    \"\"\"A univariate polynomial with a ring modulus x^N + 1.\"\"\"\n\n    def __init__(self, coefficients: np.ndarray, modulus_degree: int):\n        self.coefficients = coefficients\n        self.modulus_degree = modulus_degree\n        # ... <validations> ...\n\n    # ... __eq__, __repr__, etc. ...\n```\n\nThe above doesn’t include any actual polynomial operators yet, since these functions will mutate the underlying coefficients directly.\n\n``` php\ndef canonical_embedding(poly: Polynomial) -> np.ndarray:\n    \"\"\"Computes the canonical embedding of a polynomial.\"\"\"\n    poly_coeffs = poly.coefficients\n    N = poly.modulus_degree\n\n    # 2N-point FFT evaluates at all 2N-th roots: omega^0, omega^1, ...,\n    # omega^{2N-1}. But return only the odd entries for the primitive roots.\n    padded = np.concatenate([poly_coeffs, np.zeros(N)])\n    fft_result = np.fft.ifft(padded) * (2 * N)\n    return fft_result[np.arange(1, 2 * N, 2)]\n```\n\nThis method is slightly inefficient: to get the `numpy.fft.fft/ifft`\n\nfunctions to correspond to evaluations at $2N$-th roots of unity,\nwe need to have a vector of length $2N$ as input. Then afterward\nto get the odd evaluations, we need to filter by the appropriate range.\n\nTo be more efficient, production CKKS implementations\nimplement a custom FFT routine here that avoids this extra work\nin two ways: first by operating on the odd powers directly,\nand second taking advantage of the additional conjugate symmetry\nof the odd powers. For one such reference, see the\n[FFTSpecial](https://github.com/openfheorg/openfhe-development/blob/1306d14f8c26bb6150d3e6ad54f28dfe1007689e/src/core/lib/math/dftransform.cpp#L241)\ninvocation in\n[OpenFHE’s CKKS encoding routine](https://github.com/openfheorg/openfhe-development/blob/1306d14f8c26bb6150d3e6ad54f28dfe1007689e/src/pke/lib/encoding/ckkspackedencoding.cpp#L238),\nwhich implements Algorithm 1 of\n[Chen-Chillotti-Song 2018](https://eprint.iacr.org/2018/1043).[8](#fn:8)\n\n[Commit 386f028](https://github.com/j2kun/ckks-tutorial/pull/2/changes/386f028f079c91968a90846f80500321c60930b7)\nalso includes a more direct implementation using a matrix-vector multiplication\nby the [Vandermonde matrix](https://github.com/j2kun/ckks-tutorial/pull/2/changes/fb58601d16e55bc8a5a4ce3c0a99c1e10d879329#diff-3e74404a7218b087c3cd6ff47dd023a496e36d441a1d7fe9cbcdec91aa8b05ccR77).\nIn the tests for this commit,\nwe include equivalence testing of the two methods.\n\n## Encoding and decoding\n\nWith all the hard work done, we turn to encoding. The inverse of the canonical embedding allows us to map a complex (Hermitian) vector to a polynomial with real coefficients. However, to get a polynomial with integer coefficients (our desired plaintext space), we need to round. That raises the question of precision.\n\nCKKS’s solution is the same as traditional fixed-point arithmetic. That is, we choose a scaling factor $\\Delta$, and multiply the polynomial’s coefficients by $\\Delta$ before rounding to the nearest integer. The message can be recovered by dividing by $\\Delta$.\n\nFixed-point arithmetic will have major implications for CKKS. Specifically, it introduces an application-dependent decision for how to set parameters. Applications that can afford to be a bit less precise (like neural networks) can use smaller scaling factors, which leads to more efficient programs. We will return to this topic in fine detail later. For now, it gives us our final encoding algorithm:\n\n- Apply the inverse canonical embedding.\n- Multiply by the scale $\\Delta$.\n- Round to the nearest integer mod $Q$.\n\nBecause scaling by $\\Delta$ is followed by rounding mod $Q$, the choice of $Q$ implies a limit to the choice of $\\Delta$: the scaled coefficients after step 2 cannot exceed $Q/2$, or else they will wrap around mod $Q$ and the original value will be lost.\n\nThe code in [commit 2410f9d](https://github.com/j2kun/ckks-tutorial/pull/2/changes/2410f9da9781752b399bc589abd1df03dc4a6ea7)\ndemonstrates this.\n\nFirst we have a dataclass for parameters\n\n```\nCleartext = np.ndarray\nPlaintext = Polynomial\n\n@dataclass(frozen=True)\nclass EncodingParams:\n    scale: float\n    poly_modulus_degree: int\n```\n\nThen encoding is\n\n``` php\ndef encode(message: Cleartext, params: EncodingParams) -> Plaintext:\n    \"\"\"Encode a vector of complex numbers into a plaintext polynomial.\"\"\"\n    # Pad with zeros up to N / 2\n    num_zeros = params.poly_modulus_degree // 2 - message.shape[0]\n    if num_zeros:\n        message = np.concatenate(\n            [message, np.zeros(num_zeros, dtype=message.dtype)]\n        )\n\n    # Concat with flipped conjugate to make Hermitian\n    hermitian_msg = np.concatenate(\n        [message, np.flip(np.conjugate(message))]\n    )\n\n    # Result of inverse canonical_embedding is guaranteed to\n    # be real-valued.\n    polynomial = inverse_canonical_embedding(hermitian_msg)\n    rounded_scaled_coeffs = np.round(\n        np.real(polynomial.coefficients) * params.scale\n    )\n    return Polynomial(rounded_scaled_coeffs, params.poly_modulus_degree)\n```\n\nSimilarly, decoding divides by the scale, applies the canonical embedding, and then returns the first $N/2$ slots.\n\n``` php\ndef decode(plaintext: Plaintext, params: EncodingParams) -> Cleartext:\n    \"\"\"Decode a CKKS plaintext into a vector of complex numbers.\"\"\"\n    scale_removed = Polynomial(\n        coefficients=plaintext.coefficients / params.scale,\n        modulus_degree=plaintext.modulus_degree,\n    )\n    unembedded = canonical_embedding(scale_removed)\n    return unembedded[: params.poly_modulus_degree // 2]\n```\n\n## Investigating precision\n\nTo understand the precision loss of CKKS encoding in a bit more detail, let’s plot it.\n\n[Commit 0df5650](https://github.com/j2kun/ckks-tutorial/pull/2/changes/0df56503d2490810d9032279b3b59cab62e0ff79)\nprovides the code, and for $N=32$ with scaling factors ranging from\n$2$ to $2^{40}$ (the latter is a typical CKKS scaling factor seen in the wild),\nthis is the plot of absolute and relative errors.\n\nThe top plot shows that absolute precision scales linearly with the scaling factor. The bottom plot shows the relative deviation from the theoretical bound, and even for a smallish $N=32$ the fit is pretty good. The “theoretical” line plotted corresponds to the average expected root-mean-squared precision loss, by following heuristic reasoning.\n\nFor $N$ random values, rounding introduces a uniform error in $[-0.5, 0.5]$ for each of the coefficients. Decoding sums these errors, and the resulting distribution is Gaussian with mean roughly $\\sqrt{N}$.\n\nWe can also plot this as $N$ grows ([commit 090147f](https://github.com/j2kun/ckks-tutorial/pull/2/changes/090147f2b4a08abea7953f5284e838ad8986c8af)).\n\nThe theoretical bound still holds, except that when $N$ is sufficiently large (and when the scaling factor is sufficiently large), then, as best I can tell, the floating point errors in the FFT routine itself are of comparable magnitude to the precision loss due to rounding, which would explain the positive bias in error. At least, if I reduce the scaling factor to $2^{20}$, this positive bias disappears:\n\n## Wrapping it up\n\nRecapping, the two obstacles we faced at the start of the article were:\n\n- We must transform a vector of complex numbers into a polynomial.\n- We must discretize an inherently continuous quantity, complex numbers.\n\nThe canonical embedding solves the first obstacle by providing an isomorphism between vectors of complex numbers and polynomials. The use of fixed-point arithmetic and a scaling factor solves the second.\n\nWhile CKKS encoding does introduce errors in its encoding process, the standard\nintended application 9 of CKKS is to machine learning inference. In this context,\nprograms are naturally tolerant of error, and the encoding error is a small\none-time cost. As we will see throughout the rest of the tutorial, encoding\nerror will become dominated by rescaling and bootstrapping noise. But either\nway, it’s important not to forget about this source of error.\n\n## Acknowledgements\n\nThanks to [Asra Ali](https://scholar.google.com/citations?user=ztrus-YAAAAJ&hl=en),\n[Edward Chen](https://edwjchen.com/),\n[Jianming Tong](https://jianmingtong.github.io/), and\n[Hongren Zheng](https://hongrenzhe.ng/) for feedback on a draft of this\narticle.\n\nUC San Diego is involved because Miran Kim was doing a postdoc there at the time. Now she is a professor at Hanyang University in South Korea. I bring this up mainly to note that Korea has been a powerhouse of innovation in homomorphic encryption for the past decade, and particularly for its contributions to CKKS, its variants, and the variety of startups that have sprung up around it.\n\n[↩︎](#fnref:1)As my colleague Hongren Zheng reminded me, the original high-precision modulus for CKKS was a large power of two. So the switch to RNS-CKKS also required switching to a modulus that was a product of machine-word-sized primes.\n\n[↩︎](#fnref:2)As far as I can tell, except for some specialized research papers or hand-compiled applications, the extra complex structure is not used in applications. That is, most CKKS users treat the cleartext space as a vector of double-precision floating point values.\n\n[↩︎](#fnref:3)Indeed, if you’re paying attention to any of the work on post-quantum cryptography (which all of FHE is built on top of), you’ll see this ring again in the Kyber/ML-KEM scheme, though it is important to note that the choice of parameters $Q, N$ are meaningfully different there.\n\n[↩︎](#fnref:4)Many math texts give the existential definition “the smallest field containing $\\mathbb{Q}$ and the added elements.”\n\n[↩︎](#fnref:5)This is basically a first course in Galois theory. I enjoyed\n\n[Ian Stewart’s textbook](https://amzn.to/3OllR79)on the subject. But if you squint you can kind of see it: $x^N + 1$ is a factor of $x^{2N} - 1 = (x^N-1)(x^N+1)$, and the complex $2N$-th roots of unity are the roots of $x^{2N} - 1$, with the primitive root generating all the rest and being (any one of) the roots of the irreducible part $x^N + 1$. This irreducibility requires $N$ is a power of two, but you can do the same logic for any $N$ if you spend more time working out the $N$-th cyclotomic polynomial.[↩︎](#fnref:6)It may be worth a sneak preview here: the choice of which primitive root and the order of the evaluations is arbitrary, and choosing a different root and order will be useful for CKKS in that it will allow us to define\n\n*slot rotation*as a homomorphic operation. At that point we’ll have to make some slight tweaks to the encoding algorithm here. Having a precise diff of the code changes will make those differences clearer later, I hope.[↩︎](#fnref:7)Interestingly, Algorithm 1 was designed for evaluating the encoding/decoding algorithm of CKKS\n\n*homomorphically*(as part of bootstrapping, which we’ll see later in this series in detail), but the referenced OpenFHE code uses it for cleartext evaluation for encoding.[↩︎](#fnref:8)For more, see\n\n[FHE in production](/fhe-in-production/).[↩︎](#fnref:9)\n\nWant to respond? [Send me an email](mailto:mathintersectprogramming@gmail.com),\n[post a webmention](https://webmention.io/www.jeremykun.com/webmention),\nor find me [elsewhere on the internet](/about/).\n\nThis article is syndicated on:", "url": "https://wpnews.pro/news/ckks-polynomials-the-canonical-embedding-and-encoding", "canonical_source": "https://www.jeremykun.com/2026/04/29/ckks-polynomials-the-canonical-embedding-and-encoding/", "published_at": "2026-04-29 12:25:44+00:00", "updated_at": "2026-05-20 01:36:08.152354+00:00", "lang": "en", "topics": ["cybersecurity", "research", "data"], "entities": ["CKKS", "Ronny Ko"], "alternates": {"html": "https://wpnews.pro/news/ckks-polynomials-the-canonical-embedding-and-encoding", "markdown": "https://wpnews.pro/news/ckks-polynomials-the-canonical-embedding-and-encoding.md", "text": "https://wpnews.pro/news/ckks-polynomials-the-canonical-embedding-and-encoding.txt", "jsonld": "https://wpnews.pro/news/ckks-polynomials-the-canonical-embedding-and-encoding.jsonld"}}