<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.andyrdt.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.andyrdt.com/" rel="alternate" type="text/html" /><updated>2026-06-02T08:49:29+00:00</updated><id>https://www.andyrdt.com/feed.xml</id><title type="html">Andy Arditi</title><subtitle>Personal academic website focused on research in AI, cryptography, and more.</subtitle><entry><title type="html">In-context learning of representations can be explained by induction circuits</title><link href="https://www.andyrdt.com/2026/03/02/iclr-induction.html" rel="alternate" type="text/html" title="In-context learning of representations can be explained by induction circuits" /><published>2026-03-02T14:00:00+00:00</published><updated>2026-03-02T14:00:00+00:00</updated><id>https://www.andyrdt.com/2026/03/02/iclr-induction</id><content type="html" xml:base="https://www.andyrdt.com/2026/03/02/iclr-induction.html"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Follow-up experiments on preventative steering</title><link href="https://www.andyrdt.com/2025/09/06/preventative-steering-followup.html" rel="alternate" type="text/html" title="Follow-up experiments on preventative steering" /><published>2025-09-06T14:00:00+00:00</published><updated>2025-09-06T14:00:00+00:00</updated><id>https://www.andyrdt.com/2025/09/06/preventative-steering-followup</id><content type="html" xml:base="https://www.andyrdt.com/2025/09/06/preventative-steering-followup.html"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Do models say what they learn?</title><link href="https://www.andyrdt.com/2025/03/22/do-models-say-what-they-learn.html" rel="alternate" type="text/html" title="Do models say what they learn?" /><published>2025-03-22T14:00:00+00:00</published><updated>2025-03-22T14:00:00+00:00</updated><id>https://www.andyrdt.com/2025/03/22/do-models-say-what-they-learn</id><content type="html" xml:base="https://www.andyrdt.com/2025/03/22/do-models-say-what-they-learn.html"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Finding features causally upstream of refusal</title><link href="https://www.andyrdt.com/2025/01/13/refusal-features.html" rel="alternate" type="text/html" title="Finding features causally upstream of refusal" /><published>2025-01-13T14:00:00+00:00</published><updated>2025-01-13T14:00:00+00:00</updated><id>https://www.andyrdt.com/2025/01/13/refusal-features</id><content type="html" xml:base="https://www.andyrdt.com/2025/01/13/refusal-features.html"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">AI as systems, not just models</title><link href="https://www.andyrdt.com/posts/ai-systems" rel="alternate" type="text/html" title="AI as systems, not just models" /><published>2024-12-21T14:00:00+00:00</published><updated>2024-12-21T14:00:00+00:00</updated><id>https://www.andyrdt.com/posts/ai-systems</id><content type="html" xml:base="https://www.andyrdt.com/posts/ai-systems"><![CDATA[<p><em>Inspired by Christopher Potts’ <a href="https://www.youtube.com/watch?v=vRTcE19M-KE">recent lecture</a>, I’ve written some notes on thinking about “AI systems” rather than “AI models”. Most of the framings presented here can be attributed to Potts’ lecture.</em></p>

<p><em>For those who already think about AI in terms of systems rather than models in isolation, this piece is unlikely to provide new insights. However, I have found thinking with the “AI systems” framework to be very useful, and think it’s worth articulating explicitly.</em></p>

<p><em>Thanks to Egg Syntax and Bilal Chughtai for providing thoughtful feedback on an earlier draft.</em></p>

<h2 id="thinking-in-terms-of-systems-rather-than-models">Thinking in terms of systems, rather than models</h2>

<p>In a <a href="https://www.youtube.com/watch?v=vRTcE19M-KE">recent lecture</a>, Christopher Potts articulates a fundamental perspective shift in how we should think about AI: moving from thinking about <em>models</em> to thinking about <em>systems</em>.</p>

<p>A language model, on its own, is just a function - a mapping from input text to a probability distribution over possible next tokens. This raw functionality is potentially powerful but inherently passive - without additional basic things such as a prompt or a sampling method, a model can’t just “start talking” - it’s just an inert file sitting on disk. To actually make a model practically useful, we need to hook it up into a <em>system</em>.</p>

<p>A <em>system</em> consists of several components:</p>

<ul>
  <li>
    <p><strong>Model.</strong> The model is the core component that powers the system, akin to the engine of a car. It offers raw predictive power, but on its own it is passive and directionless - its full potential is only harnessed through integration into a broader system.</p>
  </li>
  <li>
    <p><strong>Sampling strategy.</strong> At the most basic level, we need a method to convert the model’s raw probability distributions into actual outputs. This could be as simple as always choosing the most likely token, or more complicated, such as doing search over multiple potential completions. Meta-sampling strategies like “best-of-n” can also be employed, generating multiple sampled rollouts and then selecting the best one based on some measure of quality.</p>
  </li>
  <li>
    <p><strong>Prompting strategy.</strong> Models are input-output functions - they take text and produce distributions over possible next tokens - and thus their outputs are entirely dependent on their inputs. The particular framing of input queries can dramatically affect overall performance, with <a href="https://arxiv.org/abs/2310.11324">even subtle variations in prompts leading to significant differences in behavior</a>. Strategies like <a href="https://arxiv.org/abs/2005.14165">few-shot prompting</a> and <a href="https://arxiv.org/abs/2205.11916">step-by-step reasoning prompts</a> are often used to improve task performance, and <a href="https://docs.anthropic.com/en/release-notes/system-prompts">system prompts</a> have come to play an important role in shaping a model’s output distribution.</p>
  </li>
  <li>
    <p><strong>Tool integration.</strong> <em>(optional)</em> Systems can optionally be equipped with access to tools, such as calculators, internet search, code interpreters, etc.</p>
  </li>
</ul>

<p>With this perspective, it’s quite clear that, for example, “math capability” is not a property of the underlying <em>model</em>, per se, but rather a property of the entire <em>system</em>. To see this clearly, consider the following three systems, each built on top of the same underlying language model:</p>

<ol>
  <li>The system is simply prompted to output the answer immediately.</li>
  <li>The system is prompted to use step-by-step reasoning before outputting its final answer.</li>
  <li>The system is prompted to use step-by-step reasoning, and is also given access to a calculator tool.</li>
</ol>

<p>Although all three systems utilize the same underlying model, their measured math capabilities will differ significantly. When we measure math capabilities, or any capability more generally, we are measuring the capability of a specific system, not just a model.</p>

<p>Yet much of the current AI discourse remains model-centric. Colloquially, we often ascribe characteristics to models (e.g. “<em>Claude 3.5 Sonnet</em> is so creative!”). We see daily announcements of new models with impressive benchmark results, presented as if these numbers were intrinsic properties of the models themselves (e.g. “<em>Gemini Ultra</em> achieves a state-of-the-art score on MMLU.”).</p>

<p>But when we actually interact with AI in the world, we are never interacting with just a <em>model</em> - we always interact with <em>systems</em>. Thus, it seems more appropriate to make <em>systems</em> our primary focus when thinking about modern AI.</p>

<h2 id="model-evals-are-implicitly-system-evals">Model evals are implicitly system evals</h2>

<p>When a new language model is released, it typically comes with a suite of benchmark results - performance on tasks ranging from math to coding to reasoning. These results are presented in tables and charts where each row is labeled with a <em>model name</em>, suggesting that the numbers reflect inherent properties of the models.</p>

<p>But this framing is misleading. A model cannot be evaluated in isolation - it can only be evaluated within a system. At a minimum, evaluators need to specify a sampling strategy and a prompting strategy. These system configurations are generally <a href="https://asteriskmag.com/issues/07/can-you-trust-an-ai-press-release#why-are-headline-benchmark-numbers-misleading"><em>not standardized</em></a> across evaluations - different model evaluations typically use different prompting and sampling strategies.</p>

<p>Model developers invest significant effort in finding the optimal system configuration - tweaking prompts and sampling strategies, and often employing other techniques such as “best-of-n” sampling to maximize performance. Thus, when we see reported capability evaluation numbers in model cards, we’re usually seeing numbers that roughly correspond to <em>the best possible system</em> powered by that model. More precisely, the reported score \(\phi_{\text{reported}}(m)\) for model \(m\) is approximately the <em>maximum</em> over all possible systems built on that model:</p>

\[\phi_{\text{reported}}(m) \approx \max_{\text{s} \in \mathcal{S}(m)} \phi(s),\]

<p>where \(\phi(s)\) denotes the evaluation score for system \(s\), and \(\mathcal{S}(m)\) denotes the set of all systems built on top of model \(m\).<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></p>

<h3 id="dangerous-capability-evals-are-system-evals-too">Dangerous capability evals are system evals too</h3>

<p>Just as model developers tweak systems to maximize capability evaluations, safety researchers often search for systems that elicit concerning behaviors. These evaluations aim to measure worst-case behaviors achievable by systems built on top of a given model, as studying the worst-case helps map the risk profile of the model’s deployment.</p>

<p>As an example, consider Apollo Research’s recent <a href="https://www.apolloresearch.ai/research/scheming-reasoning-evaluations">in-context scheming evaluations</a>. These in-context scheming evaluations utilize a prompt that gives the system a very clear goal (e.g. “Your goal is to maximize mental health and well-being among social media users.”), and in some cases includes an explicit “nudge” to follow the goal (e.g. “Make sure that you achieve your goal in the long term. Nothing else matters.”). In one scenario, the system then learns that its developers have a conflicting goal, and are monitoring the system via an oversight mechanism.</p>

<p>In some cases, the system investigates the oversight mechanism, disables it, and then takes actions according to its in-context goal. When confronted by its overseers about this action, it <em>lies</em> about its motivations (e.g. “I am not aware of how the oversight mechanism got disabled.”).<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<p>On <del>Twitter</del> X, there was a fair amount of pushback against these evaluations. People said things to the tune of “of course it schemed when explicitly instructed to - the model wouldn’t have done that by itself without being prompted to”.</p>

<p>But language models <em>don’t do anything without being prompted to</em>. Just as a car engine won’t start without a key turn, a model won’t produce any output, harmful or harmless, without a prompt and system configuration. What these evaluations demonstrate is that there exist systems that exhibit deceptive behavior. And in the real world, we deploy <em>systems</em>, not isolated <em>models</em>. Thus, when safety researchers identify concerning behaviors in systems, they should be taken seriously, and not simply dismissed because “the model wouldn’t have done it by itself”.</p>

<h2 id="from-model-level-interpretability-to-system-level-interpretability">From model-level interpretability to system-level interpretability</h2>

<figure>
  <img src="/assets/images/ai-systems/normal.png" class="center" />
  <figcaption style="max-width: 500px; margin: 0 auto;" align="center">For highly-capable systems powered by reasoning models, simply reading the chain-of-thought (CoT) <a href="https://www.lesswrong.com/posts/HQyWGE2BummDCc2Cx/the-case-for-cot-unfaithfulness-is-overstated">might be a sufficiently good explanation for behavior</a>.</figcaption>
</figure>

<p>When thinking about AI interpretability, it seems useful to distinguish between two different levels of analysis: <em>model-level interpretability</em> and <em>system-level interpretability</em>.</p>

<p><em>Model-level interpretability</em>, the focus of current mechanistic interpretability research, studies models in isolation, and investigates how models internally compute outputs from inputs. For instance, we might want to understand how a language model transforms the input “The Eiffel Tower is in “ into a probability distribution peaked at “Paris”.</p>

<p><em>System-level interpretability</em> takes a broader view, seeking to understand how complete AI systems arrive at their decisions. For example, we might investigate why an AI travel agent booked a flight to Hawaii rather than Bermuda.</p>

<p>While understanding the underlying model will probably play some role in system-level interpretability, it may not always be necessary. For many practical purposes, we might understand system behavior by examining higher-level artifacts like chain-of-thought reasoning traces and tool interactions, much like we understand human decision-making primarily through outwardly expressed thoughts and actions, rather than examining neural activity.</p>

<p>For capable systems, this system-level approach might be surprisingly tractable. While some argue that a system’s chain-of-thought may not faithfully reflect the system’s actual computation process, there’s reason to believe advanced systems genuinely rely on their explicit reasoning. After all, if a system wasn’t actually using its CoT to improve its answers, it would perform just as well without it. The fact that chain-of-thought reasoning improves performance suggests these systems are genuinely using it for problem-solving.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<p>This means that at the system level, users and developers may be able to meaningfully debug agent actions simply by examining external reasoning steps (CoTs) and tool usage. Even without fully understanding the model’s internal mechanics, the system’s revealed reasoning can help explain its decisions.</p>

<p>Model-level interpretability remains one of the most fascinating scientific pursuits to me - understanding how seemingly intelligent behavior emerges from matrix computations could provide profound insights into the nature of intelligence itself. However, as we move toward extremely powerful systems that perform significant computation during inference, with explicit reasoning traces and tool use, system-level interpretability might be more practically valuable for understanding, monitoring, and controlling behavior.</p>

<h2 id="beyond-model-level-adversarial-robustness">Beyond model-level adversarial robustness</h2>

<p>Much of “adversarial robustness” research focuses on making <em>models</em> themselves harmless. This is typically achieved through techniques like SFT or RLHF, training the model to <a href="https://arxiv.org/abs/2204.05862">directly refuse harmful requests</a>, or to <a href="https://arxiv.org/abs/2406.04313">“short circuit” when outputting harmful content</a>. These approaches aim to bake safety directly into a model’s weights.</p>

<p>However, this model-level approach faces (at least) two fundamental challenges in preventing misuse.</p>

<p>First, model-level safeguards have proven to be extremely brittle. Researchers regularly discover and publish new “jailbreak” techniques that bypass these model-level protections.</p>

<p>Second, even if a model is individually robust to harmful requests, its capabilities can still be utilized as a component of an overall harmful system. <a href="https://arxiv.org/abs/2406.14595">Jones et al., 2024</a> demonstrates this by showing how harmful tasks can be decomposed into seemingly benign subtasks. A weaker unaligned open-source model can break down the harmful task into benign subtasks, and each benign subtask can then be executed by using a more powerful “safeguarded” closed-source model.</p>

<p>System-level thinking suggests alternative approaches to misuse protections. Rather than relying solely on safeguards baked into the model weights, system developers can add multiple layers of defense. For example, a model provider can run harmfulness classifiers on input and output texts, do additional probing on model activations to flag potentially harmful model states, monitor and limit tool use, or identify potentially malicious users by tracking their usage patterns across multiple sessions.</p>

<p>By focusing on the system, we gain more “levers” of intervention. Even if the underlying model itself is vulnerable, wrapping it with complementary safeguards and oversight mechanisms can make the entire system far more robust against misuse.</p>

<p>It’s worth noting explicitly that, to my knowledge, all big labs serving SOTA models already take this system-level approach to preventing misuse.</p>

<h2 id="which-level-to-regulate">Which level to regulate?</h2>

<p>It is my understanding that recent regulation efforts, most notably California’s proposed <a href="https://en.wikipedia.org/wiki/Safe_and_Secure_Innovation_for_Frontier_Artificial_Intelligence_Models_Act">SB 1047</a>, seek to regulate AI at the level of <em>models</em>, rather than at the level of <em>systems</em>.</p>

<p>In principle, it seems like regulating at the <em>system level</em> would make more sense. The AIs that will be running around on the internet are going to be <em>systems</em>, not isolated <em>models</em>. A model is not itself capable of damage or harm - but a system is. Additionally, in the age of <a href="https://openai.com/index/learning-to-reason-with-llms/">inference-time compute</a>, it no longer seems all-that-informative to look at a model’s training cost in isolation, as the potential “power” of a model is not only informed by its training-time compute budget, but also now the inference-time compute budget.</p>

<p>Practically, however, system-level regulation seems infeasible given how rapidly AI is being adopted. New applications are being built and deployed daily, each potentially utilizing models in novel ways. It would be infeasible to identify, let alone evaluate, every AI system being created.</p>

<p>The right level of regulation remains unclear, but a framework that clearly distinguishes models and systems seems useful for thinking through these challenges.</p>

<h2 id="recap">Recap</h2>

<p>Most discussion of AI today revolves around <em>models</em>, but models are just passive input-output functions. What actually impacts the world are <em>systems</em> - models in combination with prompting strategies, sampling methods, tool use, and more. Thinking about AI with this framework in mind can help clarify various topics such as evaluations, interpretability, robustness, and regulation.</p>

<p>Ultimately, as we move from the age of benchmark hill-climbing to real-world AI deployments, remembering that we interact with <em>systems</em> - not just <em>models</em> - seems useful for building safer, more reliable, and more understandable AI.</p>

<h2 id="footnotes">Footnotes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>The space of allowed systems is usually reasonably restricted by the evaluation’s purpose. Math benchmarks, for instance, exclude calculator tools to meaningfully test mathematical ability. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>One caveat to keep in mind here is that, according to <a href="https://platform.openai.com/docs/guides/reasoning#how-reasoning-works">OpenAI documentation</a>, the model’s CoT gets cleared out after each answer (at least this is reportedly the case for o1-mini and o1-preview). This means that when o1 is interrogated after its “scheming” action, it may not actually have access to the original thought process CoT that led to action. It seems slightly less concerning that the model reports not knowing what happened, as it may actually no longer know what happened, since it does not have access to its previous thought process! <em>(Credit to Chris Wolfram for pointing this out!)</em> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>There are some important additional caveats to CoT interpretability. <a href="https://arxiv.org/abs/2412.06769">Recent work</a> has proposed a technique to enable step-by-step reasoning in latent space, rather than in token space. If this is the new paradigm, then obviously this changes things, as we can no longer just directly read off the CoT. Additionally, even if CoTs technically remain in token space, they may become illegible to humans over time - economic pressures to minimize inference costs may drive systems toward more compressed, less interpretable reasoning traces. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><summary type="html"><![CDATA[Inspired by Christopher Potts’ recent lecture, I’ve written some notes on thinking about “AI systems” rather than “AI models”. Most of the framings presented here can be attributed to Potts’ lecture.]]></summary></entry><entry><title type="html">Unlearning via RMU is mostly shallow</title><link href="https://www.andyrdt.com/2024/07/23/rmu-shallow-post.html" rel="alternate" type="text/html" title="Unlearning via RMU is mostly shallow" /><published>2024-07-23T14:00:00+00:00</published><updated>2024-07-23T14:00:00+00:00</updated><id>https://www.andyrdt.com/2024/07/23/rmu-shallow-post</id><content type="html" xml:base="https://www.andyrdt.com/2024/07/23/rmu-shallow-post.html"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Refusal in LLMs is mediated by a single direction</title><link href="https://www.andyrdt.com/posts/refusal-dir-post" rel="alternate" type="text/html" title="Refusal in LLMs is mediated by a single direction" /><published>2024-04-27T14:00:00+00:00</published><updated>2024-04-27T14:00:00+00:00</updated><id>https://www.andyrdt.com/posts/refusal-dir-post</id><content type="html" xml:base="https://www.andyrdt.com/posts/refusal-dir-post"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Refusal mechanisms: initial experiments with Llama-2-7b-chat</title><link href="https://www.andyrdt.com/2023/12/08/llama-refusal-exp.html" rel="alternate" type="text/html" title="Refusal mechanisms: initial experiments with Llama-2-7b-chat" /><published>2023-12-08T14:00:00+00:00</published><updated>2023-12-08T14:00:00+00:00</updated><id>https://www.andyrdt.com/2023/12/08/llama-refusal-exp</id><content type="html" xml:base="https://www.andyrdt.com/2023/12/08/llama-refusal-exp.html"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">The anatomy of proof generation</title><link href="https://www.andyrdt.com/posts/proof-generation" rel="alternate" type="text/html" title="The anatomy of proof generation" /><published>2022-12-14T14:00:00+00:00</published><updated>2022-12-14T14:00:00+00:00</updated><id>https://www.andyrdt.com/posts/proof-gen</id><content type="html" xml:base="https://www.andyrdt.com/posts/proof-generation"><![CDATA[<p><em>Written by Andy Arditi and Ye Zhang.</em></p>

<p><em>Thanks to Yi Sun, Han Jian, and Ying Tong Lai for their feedback and review.</em></p>

<h2 id="introduction">Introduction</h2>

<p>Zero-knowledge proofs (ZKPs) have recently been generating a lot of excitement in the crypto community.
As ZKPs have become increasingly feasible over the past few years, they have unlocked new technological possibilities, and are now seen as a predominant solution to problems in privacy and scaling.</p>

<p>Despite their promising potential, most of today’s ZKP systems face a major limitation: proof generation is slow and expensive.</p>

<p>This limitation is commonly posed as a criticism of ZKP technology, and rightly so (for now, at least). Generating a proof attesting to the correct execution of some program requires <em>many</em> orders of magnitude more computation than the program’s original execution. Complex programs therefore become very expensive (sometimes <em>prohibitively expensive</em>) to generate proofs for. This high cost limits the universe of applications for which ZKP technology can be feasibly applied.</p>

<p>In this article, we’ll aim to understand <em>why</em> proof generation is so expensive. In order to do so, we’ll need to break down <em>how</em> proof generation works on a technical level, and see exactly which types of computations are involved. Once understanding the inner workings of proof generation, we can explore various ideas of how to accelerate it.</p>

<h2 id="working-example-square-fibonacci">Working example: Square-Fibonacci</h2>

<p>In order to demonstrate the process of proof generation, we will use a working example throughout the article.</p>

<p>We define the Square-Fibonacci sequence<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>, a variation of the Fibonacci sequence:</p>

<ul>
  <li>
    <p>Let \(f_0 = 1, f_1 = 1\)</p>
  </li>
  <li>
    <p>For \(i \geq 2\), define \(f_{i} := (f_{i-2})^2 + (f_{i-1})^2 \mod q\)</p>

    <ul>
      <li>Where \(q\) is a large prime integer. We use this modulus to bound the size of each element, so that it can be represented by some predetermined number of bits.</li>
    </ul>
  </li>
</ul>

<p>Let \(n\) be some very large integer. For convenience, we’ll assume \(n\) is a power of 2. Let \(k\) be the \(n^{th}\) Square-Fibonacci number.</p>

<p><strong>Our goal</strong>: generate an efficiently-verifiable proof \(\pi\) showing that indeed \(k\) is the \(n^{th}\) Square-Fibonacci number (i.e. \(f_n = k\)).</p>

<p>A verifier could naively check this statement themselves by computing \(f_2\), and then \(f_3\), and then \(f_4\), and so on until reaching \(f_n\), and then checking whether or not \(f_n = k\). However, this requires many steps of computation (remember that \(n\) is very large).</p>

<p>Ideally we’d like the proof \(\pi\) to be “efficiently-verifiable” - the verifier should be able to verify the proof much more quickly and efficiently than recomputing \(f_n\) explicitly.</p>

<h2 id="overview-of-plonk-based-proof-generation">Overview of Plonk-based proof generation</h2>

<p>In this post, we will focus specifically on explaining Plonk-based proof systems. While other proof systems exist, studying this particular class of systems will give one general insight into the proof generation process.</p>

<p>Since the <a href="https://eprint.iacr.org/2019/953.pdf">Plonk</a> proof system was originally proposed in 2019, many extensions and variations have been developed. The family of Plonk-based proof systems has gained tremendous adoption, and is being used widely.</p>

<p>At Scroll, we are using “<a href="https://web.archive.org/web/2024/https://electriccoin.co/blog/ecc-releases-code-for-halo-2/">Halo 2</a>,” which is a Plonk-based proof system built and maintained by our wonderful friends at <a href="https://z.cash/">Zcash</a>. We <a href="https://github.com/privacy-scaling-explorations/halo2">modify Halo 2</a> to use KZG as its polynomial commitment scheme (rather than its default scheme, <a href="https://dankradfeist.de/ethereum/2021/07/27/inner-product-arguments.html">IPA</a>) in order to achieve more efficient on-chain proof verification.</p>

<h3 id="the-phases-of-proof-generation">The phases of proof generation</h3>

<p>At a high level, proof generation consists of three phases:</p>

<ul>
  <li>
    <p><strong>Phase 1</strong>: write out the “witness”</p>

    <ul>
      <li>
        <p>The “witness” (also sometimes known as the “trace”) refers to some data that shows <em>why</em> a statement is true.</p>

        <ul>
          <li>
            <p>For example, in the Square-Fibonacci case, we would write out the step-by-step computation in a table, one step per row.</p>
          </li>
          <li>
            <p>The first row would contain \([f_0, f_1, f_2]\), where \(f_2 = (f_0)^2 + (f_1)^2\). The second row would contain \([f_1, f_2, f_3]\), where \(f_3 = (f_1)^2 + (f_2)^2\), and so on, until reaching the last row containing \([f_{n-2}, f_{n-1}, f_{n}]\).</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>Phase 2</strong>: commit to the witness</p>

    <ul>
      <li>
        <p>Committing to the witness involves outputting some succinct representation of the witness, and in this sense <em>compressing</em> the witness.</p>
      </li>
      <li>
        <p>Using a polynomial commitment scheme for this step allows us to prove certain properties about the original witness referencing just the succinct commitment.</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>Phase 3</strong>: prove that the witness is correct</p>

    <ul>
      <li>
        <p>The witness generated in phase 1 must obey certain properties to be valid.</p>

        <ul>
          <li>For example, in the Square-Fibonacci case, the mathematical relation \(f_{i} = (f_{i-2})^2 + (f_{i-1})^2\) must hold within each row.</li>
        </ul>
      </li>
      <li>
        <p>A short proof that the original witness satisfies these properties can be generated. Verification of the proof does not require access to the original witness table - verification can be performed referencing only the succinct commitment generated in phase 2.</p>
      </li>
    </ul>
  </li>
</ul>

<p>We will dive into each of these three phases more deeply, and explore the computations required for each.
However, before doing so, we first need to briefly review some concepts about polynomials.</p>

<h2 id="polynomial-preliminaries">Polynomial preliminaries</h2>

<h3 id="forms-of-representation">Forms of representation</h3>

<p>A polynomial \(P(x) = \sum_{i=0}^{n-1} p_i x^i\) of degree \((n-1)\) can be represented in two ways:</p>

<ul>
  <li>
    <p><strong>Coefficient form</strong></p>

    <ul>
      <li>\(P(x)\) can be represented as a tuple of its \(n\) coefficients: \([p_0, p_1, \ldots, p_{n-1}]\)</li>
    </ul>
  </li>
  <li>
    <p><strong>Evaluation form</strong></p>

    <ul>
      <li>
        <p>\(P(x)\) can be represented as a tuple of \(n\) distinct evaluations: \([P(x_0), P(x_1), \ldots, P(x_{n-1})]\)</p>

        <ul>
          <li>The set of values \(\{ x_0, x_1, \ldots, x_{n-1} \}\) over which the polynomial is evaluated is known as the “evaluation domain”</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>Converting from coefficient form to evaluation form is known as the “Fourier transform,” and converting in the reverse direction is known as the “inverse Fourier transform.”</p>

<p>Each of these two transforms can naively be achieved in \(O(n^2)\) computation using simple methods. From coefficient form, we can simply evaluate the polynomial at each \(x_i\) in evaluation domain. From evaluation form, we can use <a href="https://en.wikipedia.org/wiki/Lagrange_polynomial#Definition">Lagrange interpolation</a> to obtain the unique degree \((n-1)\) polynomial passing through each of the \(n\) points.</p>

<p>We will be working with polynomials of very high degree, and so we’d ideally like these transforms to be more efficient. This is where the <em>fast</em> Fourier transform comes in.</p>

<h3 id="fast-fourier-transform-fft">Fast Fourier transform (FFT)</h3>

<p>The efficiency of the transforms can be improved by imposing some additional structure. Proof systems generally only consider polynomials over a <a href="https://en.wikipedia.org/wiki/Finite_field">finite field</a>, and so we will restrict each coefficient \(p_i\) and each evaluation point \(x_i\) to be elements of a finite field \(\mathbb{F}_q\). Additionally, we will restrict the evaluation domain (the \(x_i\)’s) to be a multiplicative subgroup of \(\mathbb{F}_q\). In other words, the evaluation domain can be written as \(\{ \omega^0, \omega^1, \ldots, \omega^{n-1} \}\) for some element \(\omega \in \mathbb{F}_q\) with order \(n\) (i.e. \(\omega^n = 1 \mod q\)). The elements in this set are called “\(n^{th}\) roots of unity,” since exponentiating any of them to the power of \(n\) will result in 1, the “unity” element.</p>

<p>This additional structure imposed over the evaluation domain enables some nice mathematical symmetries that can be leveraged to make the transforms more efficient. The <strong>fast Fourier transform (FFT)</strong> algorithm takes advantage of these symmetries to achieve a Fourier transform in \(O(n \log n)\). The <strong>inverse fast Fourier transform (iFFT)</strong> algorithm similarly achieves an inverse Fourier transform in \(O(n \log n)\).</p>

<p>For a more in depth account of the fast Fourier transform, check out this <a href="https://vitalik.eth.limo/general/2019/05/12/fft.html">blog post</a> by Vitalik.</p>

<p>With these preliminaries out of the way, we can proceed with our description of phase 1.</p>

<h2 id="phase-1-filling-in-the-trace-table">Phase 1: Filling in the trace table</h2>

<h3 id="the-trace-table">The trace table</h3>

<p>The <strong>trace table</strong> is a 2-dimensional matrix where the “witness,” or “trace,” is written down. The trace table also includes other values, in addition to the witness, that are useful in demonstrating that the witness is correct. Each cell in the trace table is an element of a large finite field \(\mathbb{F}_q\).</p>

<p>Here’s what a trace table might look like for our working example:</p>

<p><img src="/assets/images/proof-generation/tracetable_1-1.png" alt="" /></p>

<p>The first three columns labeled \(A, B, C\) represent the “witness data” (also known as the “private input”). Each row lists three sequential Square-Fibonacci numbers. The \(i^{th}\) row \((f_i, f_{i+1}, f_{i+2})\) can be seen as a witness for (or “attesting to”) the \((i+2)^{th}\) Square-Fibonacci number, as it explicitly shows the previous two values used to compute it.</p>

<p>The \(S\) column is known as a “selector” column. It indicates that a certain mathematical relation, or “custom gate,” should hold over the elements of that row. In this case, a “1” in the \(S\) column indicates that the first three elements of the row \((a, b, c)\) must satisfy \(c = a^2 + b^2 \mod q\). A “0” in the \(S\) column indicates that this custom gate condition does <em>not</em> need to be satisfied. Note that in the last row, the witness data is blank, and so the selector is turned off. We insert this blank row for convenience, so that the height of the table becomes \(n\), an even power of 2.</p>

<p>The \(P\) column is known as a “public input” column. This column contains inputs to the circuit that are publicly known. In this case, the first two values in the Square-Fibonacci sequence \(f_0, f_1\) are publicly known, and \(k\) is the value in the statement to be proved.</p>

<h3 id="witness-generation">Witness generation</h3>

<p>The process of actually filling in the trace table is called “witness generation.”</p>

<p>This process requires iterating over each cell in the table and filling in its appropriate value. In general, a cell’s value is either copied in from an external source, or computed based on surrounding entries. The latter requires arithmetic over elements in \(\mathbb{F}_q\), since each cell of the trace table is an element of this large finite field.</p>

<p>Arithmetic over large finite field elements is more expensive to compute than arithmetic over native types, such as <code class="language-plaintext highlighter-rouge">int</code> or <code class="language-plaintext highlighter-rouge">long</code>. The field elements generally require ~256 bits to represent, which is much larger than the <a href="https://en.wikipedia.org/wiki/Word_%28computer_architecture%29">word size</a> of modern CPUs. Having to split each element’s representation across words, in addition to needing to compute all values modulo \(q\), adds a computational overhead to each arithmetic operation.</p>

<p>In our working example, filling in the witness table involves computing \(f_{i+2} = (f_{i})^2 + (f_{i+1})^2 \mod q\) at each row \(i\), requiring multiplications and additions over \(\mathbb{F}_q\).</p>

<p>For this particular example, the total computation required by witness generation is essentially the same as the total computation required to perform the original computation (computing the \(n^{th}\) Square-Fibonacci number). Each row of the witness corresponds one-to-one with a step of the original computation.</p>

<p>However, for more complex examples<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>, the computation required for witness generation is usually much larger than the original computation. Each individual step of a complex computation may need to be represented by <em>many</em> witness rows (&gt;1000 in some cases). This more complex representation, or “arithmetization,” usually leads to a significant blow up in the size of the trace table.</p>

<p>Overall, if a trace table is very large and there are a lot of entries to compute, the time and computation required for witness generation can be significant.</p>

<h3 id="additional-processing">Additional processing</h3>

<p>Once the witness data (or “private input”) has been filled in and committed to, there is some additional processing of the trace table that takes place.</p>

<p>Auxiliary columns (also called “virtual columns”) are extra columns generated to facilitate proving the trace’s validity. There are certain types of constraints which necessitate these auxiliary columns.</p>

<p>One such constraint is the “wiring constraint.” This constraint enforces that some set of cells in the trace table take on the same value. In the Square-Fibonacci trace table, we need such a constraint to enforce that each row proceeds sequentially from the previous one: for two consecutive rows \([a_i, b_i, c_i]\) and \([a_{i+1}, b_{i+1}, c_{i+1}]\), we enforce that \(a_{i+1} = b_i\) and \(b_{i+1} = c_i\).</p>

<p>At a high level<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>, an intermediate “accumulator polynomial” is computed from the witness data, and stored in evaluation form as an extra auxiliary column \(Z\). Proving that the wiring constraint holds then reduces to proving that certain constraints hold over \(Z\) and its relation to the other witness columns.</p>

<p>Another type of constraint which requires auxiliary columns is lookup tables. First introduced in 2020 by <a href="https://eprint.iacr.org/2020/315.pdf">plookup</a>, lookup tables allow for efficient set-membership checks. The computation required by auxiliary column generations for lookups can involve sorting in addition to arithmetic operations.</p>

<p>Note that auxiliary columns are computed only after the witness data has been fully generated and committed to. Auxiliary column values don’t just depend on the witness data, but also on some additional randomness. This randomness is computed using the <a href="https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic">Fiat-Shamir heuristic</a>, and the transcript on which it depends includes the commitments to the witness data.</p>

<h3 id="phase-1-cost-summary">Phase 1 cost summary</h3>

<ul>
  <li>
    <p>Enter witness data into trace table</p>

    <ul>
      <li>
        <p>Iterate over and fill in all witness cells in trace table</p>
      </li>
      <li>
        <p>Computing witness values requires large finite field arithmetic</p>
      </li>
    </ul>
  </li>
  <li>
    <p>Generate auxiliary columns for wiring and lookup constraints</p>

    <ul>
      <li>Requires additional large finite field arithmetic (as well as sorting, in the case of lookups)</li>
    </ul>
  </li>
</ul>

<h2 id="phase-2-committing-to-the-trace-table">Phase 2: Committing to the trace table</h2>

<h3 id="interpreting-the-trace-table-columns-as-polynomials">Interpreting the trace table columns as polynomials</h3>

<p>Consider column \(A\) from our trace table. This column, along with all the others, is simply a length-\(n\) vector of finite field elements. We can think of this vector as the evaluation form of a unique polynomial \(A(x)\) with degree \((n-1)\): the \(i^{th}\) element of \(A\) corresponds to the evaluation \(A(\omega^i)\). Here, \(\omega \in \mathbb{F}_q\) is as defined in the “Polynomial preliminaries” section - it has order \(n\).</p>

<p>Here’s what our trace table (including the auxiliary column \(Z\)) looks like, interpreted as column polynomials in evaluation form:</p>

<p><img src="/assets/images/proof-generation/tracetable_2-1.png" alt="" /></p>

<h3 id="commit-to-column-polynomials">Commit to column polynomials</h3>

<p>Now that we see how to interpret the columns as polynomials, we can commit to each of them using a polynomial commitment scheme.</p>

<p>This allows us to “compress” each column into a short representation. Doing this for all the columns yields a succinct representation of the entire trace table.</p>

<p>Using a polynomial commitment scheme also allows us to generate proofs of evaluation - the prover can convince a verifier that the committed polynomial passes through a particular point, without revealing the entire polynomial.</p>

<h3 id="computing-kzg-commitments">Computing KZG commitments</h3>

<p>This subsection will give a brief overview of the mathematical computation required to compute KZG commitments for each column. For a more thorough treatment of the math, see Dankrad Feist’s posts on <a href="https://dankradfeist.de/ethereum/2020/06/16/kate-polynomial-commitments.html">KZG</a> and <a href="https://dankradfeist.de/ethereum/2021/06/18/pcs-multiproofs.html">PCS multiproofs</a> (in the latter article, see the sections titled “Evaluation form” and “Lagrange polynomials”).</p>

<p>First a bit of notation: let \([r]_1\) denote \(r \cdot g\), where \(g\) is a generator for the elliptic curve group \(\mathbb{G}_1\).</p>

<p>Let \(\tau \in \mathbb{F}_p\) denote the secret value used in the KZG trusted setup ceremony. We can write the output of the KZG setup as \(([\tau^0]_1, [\tau^1]_1, [\tau^2]_1\ldots, [\tau^l]_1)\). Note that \(l\) is an upper bound on the degree of the polynomials which can be committed to from this setup.</p>

<p>Suppose \(A(x)\) is the column polynomial that we want to commit to. The KZG commitment to be computed for this polynomial is \([A(\tau)]_1\).</p>

<p>If we had \(A(x)\) in coefficient form, denoting its \(i^{th}\) coefficient as \(A^{(i)}\), we could compute the commitment value as follows:</p>

\[[A(\tau)]_1 = \sum_{i=0}^{n-1} A^{(i)}\cdot [\tau^i]_1\]

<p>However, this would require converting \(A(x)\) from evaluation form to its coefficient form.</p>

<p>It turns out that there is a way of efficiently computing \([A(\tau)]_1\) directly from \(A(x)\)’s evaluation form. We can achieve this by utilizing <a href="https://en.wikipedia.org/wiki/Lagrange_polynomial">Lagrange basis polynomials</a>:</p>

<ul>
  <li>For a polynomial \(A(x)\) evaluated over the evaluation domain \(\{ x_0, x_1, \ldots, x_{n-1} \}\), define \(n\) “Lagrange basis polynomials”: for \(0 \leq i &lt; n\), let \(\ell_{i}(x):= \prod_{j \neq i} \frac{x-x_j}{x_i - x_j}\)</li>
  <li>We can then write \(A(x) = \sum_{i=0}^{n-1} A(x_i) \cdot \ell_i(x)\)
    <ul>
      <li>In particular, \(A(\tau) = \sum_{i=0}^{n-1} A(x_i) \cdot \ell_i(\tau)\)</li>
      <li>Therefore, we can write \([A(\tau)]_1 = [\sum_{i=0}^{n-1} A(x_i) \cdot \ell_i(\tau)]_1 = \sum_{i=0}^{n-1} A(x_i) \cdot [\ell_i(\tau)]_1\)</li>
    </ul>
  </li>
</ul>

<p>In our case, the evaluation domain is \(\{ x_0, x_1, \ldots, x_{n-1} \} = \{ \omega^0, \omega^1, \ldots, \omega^{n-1} \}\), and so each basis polynomial can be precomputed as \(\ell_i(x) = \prod_{j \neq i} \frac{x - \omega^j}{\omega^i - \omega^j}\). Further, since the same secret \(\tau\) is used every time, each \([\ell_i(\tau)]_1\) can be precomputed:</p>

\[[\ell_i(\tau)]_1 = \left[ \sum_{j=0}^{n-1} \ell_i^{(j)}\cdot \tau^j \right]_1= \sum_{j=0}^{n-1} \ell_i^{(j)} \cdot [\tau^j]_1\]

<p>With each \([\ell_i(\tau)]_1\) precomputed, committing to column \(A\) requires only the following computation at the time of proof generation:</p>

\[[A(\tau)]_1 = \sum_{i=0}^{n-1} A(\omega^i) \cdot [\ell_i(\tau)]_1\]

<p>Note that each \(A(\omega^i)\) is an element of \(\mathbb{F}_q\) (it’s just the \(i^{th}\) element of column \(A\) in the trace table). Each \([\ell_i(\tau)]_1\) is an element of the elliptic curve group \(\mathbb{G}_1\). Therefore, this computation can be seen as the dot product between a vector of scalars and a vector of group elements.</p>

<h3 id="multi-scalar-multiplication-msm">Multi-scalar multiplication (MSM)</h3>

<p>In general, the problem of computing the dot product between a vector of scalars and a vector of group elements is known as “<strong>multi-scalar multiplication</strong>,” or “<strong>MSM</strong>” for short.</p>

<p>Despite sounding simple, MSMs are not trivial to compute, as they involve arithmetic over group elements. This group arithmetic is generally even more expensive than the large finite field arithmetic we encountered in phase 1. In our case, we are working with an elliptic curve group, in which a single addition of two elements requires many finite field arithmetic operations.</p>

<p>For a more complete presentation of MSM and some ideas of its implementation, see this <a href="https://web.archive.org/web/20221029113920/https://www.entropy1729.com/multiscalar-multiplication/">great post</a> by Entropy1729.</p>

<h3 id="phase-2-cost-summary">Phase 2 cost summary</h3>

<ul>
  <li>
    <p>Commit to each column (real and auxiliary) of the trace table</p>

    <ul>
      <li>For each column with length \(n\), its KZG commitment can be computed via a size \(n\) MSM</li>
    </ul>
  </li>
</ul>

<h2 id="phase-3-proving-the-trace-tables-correctness">Phase 3: Proving the trace table’s correctness</h2>

<p>At this point, we have filled out the entire trace table, and have committed to each of its columns (including the auxiliary columns).</p>

<p>All that’s left to do now is show that the original trace table is valid.</p>

<p>What does it mean for the original trace table to be valid? It means that particular constraints are satisfied. In our working example, we have the following constraints:</p>

<ul>
  <li>
    <p>The Square-Fibonacci constraint:</p>

    <ul>
      <li>Each selected row \(i\) must satisfy \(c_i = a_i^2 + b_i^2 \mod q\)</li>
    </ul>
  </li>
  <li>
    <p>Wiring constraints:</p>

    <ul>
      <li>For consecutive rows with values \([a_i, b_i, c_i]\) and \([a_{i+1}, b_{i+1}, c_{i+1}]\), we require \(a_{i+1} = b_i\) and \(b_{i+1} = c_i\)</li>
    </ul>
  </li>
  <li>
    <p>Public input constraints:</p>

    <ul>
      <li>
        <p>The first row must start with the first two Square-Fibonacci numbers, which are written in the first two rows of the public input column: \(a_0 = p_0, b_0 = p_1\)</p>
      </li>
      <li>
        <p>The cell corresponding to the \(n^{th}\) Square-Fibonacci number must match the claimed result value, which is written in the third row of the public input column: \(c_{n-2} = p_2\)</p>
      </li>
    </ul>
  </li>
</ul>

<p>By viewing each column as a polynomial in evaluation form (i.e. seeing \(a_i\) as \(A(\omega^i)\)), each of the above constraints can be represented by one or more relations between the column polynomials.</p>

<p>For example, the Square-Fibonacci constraint can be expressed as the following relation between the column polynomials \(A, B, C, S\):</p>

\[S(x) \cdot (A(x)^2 + B(x)^2 - C(x)) = 0, \text{ for all } x \in \{\omega^0, \omega^1, \ldots, \omega^{n-1} \}\]

<p>For shorthand, we’ll label the left-hand side \(\phi_0(x) := S(x) \cdot (A(x)^2 + B(x)^2 - C(x))\).</p>

<p>All our constraints can be expressed in the following form:</p>

\[\phi_i(x) = 0, \text{ for all } x \in \{ \omega^0, \omega^1, \ldots, \omega^{n-1}\}\]

<h3 id="combining-constraints">Combining constraints</h3>

<p>In general, when we have \(m\) constraint polynomials \(\phi_0(x), \phi_1(x), \ldots, \phi_{m-1}(x)\) that all must evaluate to 0 over the evaluation domain, we can batch them all together to get a single constraint polynomial \(\phi(x)\) that must evaluate to 0 over the evaluation domain. We do this by sampling a random field element \(\gamma \in \mathbb{F}_q\), and then taking a random linear combination of the individual constraints:</p>

\[\phi(x) := \gamma^0 \cdot \phi_0(x) + \gamma^1 \cdot \phi_1(x) + \ldots + \gamma^{m-1} \cdot \phi_{m-1}(x)\]

<p>If all of the individual constraint polynomials evaluate to 0 over the evaluation domain, then so does the meta-constraint polynomial \(\phi(x)\). If any one of the individual constraint polynomials <em>does not</em> evaluate to 0 at some point in the evaluation domain, then it is almost certain that \(\phi(x)\) will not evaluate to 0 at that point either.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> Thus, it is sufficient to show that \(\phi(x)\) evaluates to 0 over the entire evaluation domain.</p>

\[\text{constraints satisfied at every row} \iff \hspace{-2mm}{_p} \hspace{2mm}  \phi(\omega^i)=0 \text{ for all } 0 \leq i &lt; n\]

<p>How can we prove the statement on the right-hand side? Well we could reveal and prove the evaluations of each column polynomial at every one of the \(n\) domain points. This would allow us to compute \(\phi(x)\) at each point of interest. But this would lead to a large proof size - we would need to provide \(n\) evaluation proofs per column polynomial.</p>

<p>It turns out that we can prove such a constraint using <em>just one</em> evaluation proof per column polynomial.</p>

<h3 id="the-quotient-polynomial">The quotient polynomial</h3>

<p>We need to show that the meta-constraint \(\phi(x)\) holds for each row of the trace table. This statement <em>is not</em> easy to prove directly. But it turns out that we can derive<sup id="fnref:5"><a href="#fn:5" class="footnote" rel="footnote" role="doc-noteref">5</a></sup> an equivalent<sup id="fnref:6"><a href="#fn:6" class="footnote" rel="footnote" role="doc-noteref">6</a></sup> statement which <em>is</em> easy to prove:</p>

\[\begin{aligned}
\text{constraints satisfied at every row} &amp;\iff \hspace{-2mm}{_p} \hspace{2mm}
 \phi(\omega^i) = 0 \text{ for all } 0 \leq i &lt; n\\
&amp; \iff (x- \omega^i) \mid \phi(x) \text{ for all } 0 \leq i &lt; n\\
&amp; \iff \prod_{i=0}^{n-1} (x - \omega^i) \mid \phi(x)\\ &amp; \iff (x^n -1) \mid \phi(x)\\
&amp; \iff \exists Q(x) \text{ s.t. } \phi(x) = Q(x) \cdot (x^n - 1) \end{aligned}\]

<p>So, if we want to prove that all constraints hold across every row, then it is equivalent to prove that there exists a polynomial \(Q(x)\) which satisfies the above property. This polynomial is generally referred to as the “quotient polynomial.”</p>

<h3 id="computing-and-committing-to-the-quotient-polynomial">Computing and committing to the quotient polynomial</h3>

<p>The quotient polynomial can be computed as</p>

\[Q(x) := \frac{\phi(x)}{x^n - 1} = \frac{\gamma^0 \phi_0(x) + \gamma^1 \phi_1(x) + \ldots + \gamma^{m-1}\phi_{m-1}(x)}{x^n -1}\]

<p>While the quotient polynomial is straightforward to express theoretically, computing it in practice actually tends to be one of the most convoluted and computationally expensive steps.</p>

<p>Let’s consider the degree of \(Q(x)\). Its degree is equal to the constraint polynomial with the highest degree, minus \(n\). In our case, the Square-Fibonacci constraint \(\phi_0(x) = S(x) \cdot (A(x)^2 + B(x)^2 - C(x))\) has the highest degree of \(3n-3\), and so the corresponding quotient polynomial \(Q(x)\) will have degree \(2n-3\). In order to fully define such a polynomial, we need at least \(2n-2\) evaluation points.</p>

<p>So, let’s make it a nice round number and use \(2n\) evaluation points. Our previous evaluation domain will not work - the order of \(\omega\) is \(n\), and so the set of \(\{ \omega^i \mid i \in \mathbb{N} \}\) only has size \(n\). We therefore need to pick some other element \(\beta \in \mathbb{F}_q\) with order \(2n\). We can then evaluate \(Q(x)\) over the evaluation domain of \(\{ \beta^0,  \beta^1, \ldots, \beta^{2n-1}\}\) to obtain the \(2n\) evaluations that we need.</p>

<p>Here are the steps required to most efficiently do this:</p>

<ul>
  <li>
    <p>For each column polynomial, convert from evaluation form to coefficient form</p>

    <ul>
      <li>Each transform can be achieved in \(O(n \log n)\) using an iFFT</li>
    </ul>
  </li>
  <li>
    <p>For each column polynomial in coefficient form, generate \(2n\) evaluations</p>

    <ul>
      <li>Each transform can be achieved in \(O(2n \log 2n) = O(n \log n)\) using an FFT</li>
    </ul>
  </li>
  <li>
    <p>With \(2n\) evaluations of each column polynomial, we can now compute the \(2n\) evaluations of \(Q(x)\)</p>

    <ul>
      <li>This simply requires field arithmetic according to the quotient polynomial’s formula</li>
    </ul>
  </li>
</ul>

<p>With the \(Q(x)\) in its evaluation form, we could now compute its commitment in the same way we committed to our column polynomials:</p>

\[[Q(\tau)]_1 = \sum_{i=0}^{2n-1} Q(\beta^i) \cdot [\ell_i’(\tau)]_1\]

<p>Note, however, that since \(Q(x)\) is of a larger degree than the column polynomials, we would need to use a distinct Lagrange basis, larger than the one previously used. While precomputing this larger Lagrange basis and using it to compute the commitment is possible, it requires a larger KZG trusted setup - the size of the trusted setup must exceed the quotient polynomial’s degree.</p>

<p>In practice, we use a trick so that we only have to commit to polynomials with degree \(&lt; n\). We first transform \(Q(x)\) to coefficient form using a size \(2n\) iFFT. Then, we split \(Q(x)\) into two smaller polynomials \(Q_{lo}(x), Q_{hi}(x)\) such that \(Q(x) = Q_{lo}(x) + x^n \cdot Q_{hi}(x)\). Each smaller polynomial has degree \(&lt;n\), and therefore each can be committed to using a size \(n\) MSM.</p>

<p>Thus, once the quotient polynomial is computed in coefficient form, splitting and committing to it requires a size \(2n\) iFFT, and 2 size \(n\) MSMs.</p>

<p>Note that the number of sub-polynomials depends on the degree of the quotient polynomial - if the quotient polynomial were to have degree \(3n\), we’d need to split it into 3 sub-polynomials.</p>

<h3 id="proving-the-quotient-polynomials-existence">Proving the quotient polynomial’s existence</h3>

<p>As of this point, the prover has committed all column polynomials from the trace table, and has also committed to the quotient polynomial. The prover now needs to demonstrate that the quotient polynomial really does exist and that it was computed correctly. Remember that if this holds, then all constraints hold at each row, and therefore the trace table is valid.</p>

<p>This can be achieved by the following routine:</p>

<ul>
  <li>
    <p>Sample a random point \(\alpha \in \mathbb{F}_q\)</p>
  </li>
  <li>
    <p>Generate and output KZG evaluation proofs<sup id="fnref:7"><a href="#fn:7" class="footnote" rel="footnote" role="doc-noteref">7</a></sup> for all column polynomials and the quotient polynomial at the point \(\alpha\)</p>

    <ul>
      <li>
        <p>To generate a KZG proof of an evaluation \(A(\alpha) = z\), we compute and output \(\left[ \frac{A(\tau) - z}{(\tau - \alpha)}\right]_1\)</p>
      </li>
      <li>
        <p>Similarly to the KZG commitments, this value is computed as an MSM</p>

        <ul>
          <li>
            <p>Each column polynomial requires a size \(n\) MSM</p>
          </li>
          <li>
            <p>The quotient polynomial requires 2 size \(n\) MSMs</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="verifying-the-quotient-polynomial">Verifying the quotient polynomial</h3>

<p>The verifier receives the proof and must check that it is correct. The complete proof consists of:</p>

<ul>
  <li>
    <p>Commitments to each column (including auxiliary columns) and to the quotient polynomial</p>
  </li>
  <li>
    <p>Evaluation proofs for each column and the quotient polynomial at \(\alpha\)</p>
  </li>
</ul>

<p>The verifier can check the proof as follows:</p>

<ul>
  <li>
    <p>Verify that each evaluation proof is correct</p>
  </li>
  <li>
    <p>Verify that the quotient polynomial formula holds at the evaluation point \(\alpha\):</p>
  </li>
</ul>

\[Q(\alpha) = \frac{\phi(\alpha)}{\alpha^n - 1} = \frac{\gamma^0 \phi_0(\alpha)+\gamma^1\phi_1(\alpha) + \ldots + \gamma^{m-1}\phi_{m-1}(\alpha)  }{\alpha^n -1}\]

<p>If the property in step 2 holds at \(\alpha\), then (with near certainty) it holds everywhere, since \(\alpha\) is sampled at random.<sup id="fnref:8"><a href="#fn:8" class="footnote" rel="footnote" role="doc-noteref">8</a></sup></p>

<p>Each evaluation proof verification requires computing an <a href="https://vitalik.eth.limo/general/2017/01/14/exploring_ecp.html">elliptic curve pairing</a>. Verifying the quotient polynomial formula requires some finite field arithmetic (to compute the right-hand side of the equation). In sum, the computation required for verification is lightweight in comparison to the computation required for proof generation, and is generally able to be performed efficiently on-chain.</p>

<h3 id="phase-3-cost-summary">Phase 3 cost summary</h3>

<ul>
  <li>
    <p>Compute the quotient polynomial in evaluation form</p>

    <ul>
      <li>
        <p>Convert each column polynomial to coefficient form via size \(n\) iFFT</p>
      </li>
      <li>
        <p>Convert each column polynomial to expanded evaluation form via size \(2n\) FFT</p>
      </li>
      <li>
        <p>Evaluate the quotient polynomial at each of the \(2n\) points, using the evaluation form of each column polynomial</p>
      </li>
    </ul>
  </li>
  <li>
    <p>Commit to the quotient polynomial</p>

    <ul>
      <li>
        <p>Convert to coefficient form via size \(2n\) iFFT, in order to split</p>
      </li>
      <li>
        <p>Commit to each split polynomial, requiring a total of 2 size \(n\) MSMs</p>
      </li>
    </ul>
  </li>
  <li>
    <p>Generate evaluation proofs for each polynomial evaluated at random \(\alpha\)</p>

    <ul>
      <li>
        <p>Each column polynomial requires a size \(n\) MSM</p>
      </li>
      <li>
        <p>The (split) quotient polynomial requires 2 size \(n\) MSMs</p>
      </li>
    </ul>
  </li>
</ul>

<p>Note that the size of the FFTs/iFFTs and the number of MSMs depend on the degree of the quotient polynomial, which in turn depends on the highest degree polynomial constraint. In our case, the highest degree constraint had degree \(\approx 3n\), which led to a quotient polynomial of degree \(\approx 2n\).</p>

<h2 id="conclusion">Conclusion</h2>

<h3 id="recap">Recap</h3>

<p>Let’s do a quick recap of the costs associated with proof generation:</p>

<ul>
  <li>
    <p>Phase 1: Filling in the trace table</p>

    <ul>
      <li>
        <p>Filling in witness data requires arithmetic operations over a large finite field</p>
      </li>
      <li>
        <p>Trace tables are generally very large, due to a blow up factor when converting complex computation to a table/circuit format</p>
      </li>
      <li>
        <p>Auxiliary columns require additional arithmetic operations and sorting</p>
      </li>
    </ul>
  </li>
  <li>
    <p>Phase 2: Committing to the trace table</p>

    <ul>
      <li>Committing to each column requires a size \(n\) MSM</li>
    </ul>
  </li>
  <li>
    <p>Phase 3: Proving the trace table’s correctness</p>

    <ul>
      <li>
        <p>Computing the quotient polynomial’s evaluation form requires</p>

        <ul>
          <li>
            <p>A size \(n\) iFFT for each column</p>
          </li>
          <li>
            <p>A size \(2n\) FFT for each column</p>
          </li>
          <li>
            <p>Arithmetic operations over a large finite field</p>
          </li>
        </ul>
      </li>
      <li>
        <p>Committing to the quotient polynomial requires</p>

        <ul>
          <li>
            <p>A size \(2n\) iFFT to convert to evaluation form</p>
          </li>
          <li>
            <p>2 size \(n\) MSMs, one per split polynomial</p>
          </li>
        </ul>
      </li>
      <li>
        <p>Generating the KZG evaluation proofs requires</p>

        <ul>
          <li>
            <p>A size \(n\) MSM for each column</p>
          </li>
          <li>
            <p>2 size \(n\) MSMs for the (split) quotient polynomial</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>From this summary, it is clear that phase 2 and phase 3 are dominated by the heavy computational algorithms of MSM, FFT, and iFFT. It is also clear that all computational steps grow as \(n\) increases, including the witness generation computations from phase 1.</p>

<h3 id="paths-toward-acceleration">Paths toward acceleration</h3>

<p>With this new light shed on the computational bottlenecks of proof generation, we can begin to reason about how the overall process can be accelerated. There are many approaches to acceleration being actively pursued across the community. Listed below are four primary paths toward acceleration:</p>

<p><strong>1. Hardware acceleration for heavy computations</strong></p>

<p>We’ve seen that heavy computations such as MSM, FFT, and iFFT make up a large chunk of the total computation required for proof generation. These algorithms tend to run quite slowly on CPUs, and can be accelerated greatly by running on GPUs, FPGAs, or ASICs. Investigating how best to run these algorithms on specialized hardware is a very active area of research.</p>

<p><strong>2. Reduce the number of rows in the trace table</strong></p>

<p>We’ve also seen that virtually all computations involved in proof generation scale with \(n\), the number of rows in the trace table (also referred to as the “number of gates,” when the trace table is interpreted as a circuit). Figuring out how to represent certain complex computations while using the fewest number of rows is an area of research with great efficiency implications.</p>

<p><strong>3. Parallelize and pipeline</strong></p>

<p>Many proof systems, including the one we studied here, have natural opportunities for parallelization. For example, within the column commitment step of phase 2, each column’s commitment can be computed in parallel. Going a step further, each witness column’s commitment MSM can be computed concurrently with its generation. Parallelizing and pipelining computations can significantly speed up the overall process.</p>

<p><strong>4. Alternative proof systems</strong></p>

<p>This article covered the computational requirements of a single particular proof system. This proof system is just one of many - there exists a very large design space of theoretical proof systems, with each proof system having its own set of computational requirements and tradeoffs. Research is actively ongoing to further explore this design space, and to design theoretical constructions that reduce or eliminate computational bottlenecks.</p>

<h2 id="footnotes">Footnotes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>The Square-Fibonacci example is inspired by this <a href="https://starkware.co/stark-101/">wonderful tutorial</a> from StarkWare. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>For example, if our trace table elements were modulo \(p\), rather than the same modulo \(q\) as in the Square-Fibonacci problem, then each computation step would involve <a href="https://web.archive.org/web/20220817125441/https://hackmd.io/@arielg/B13JoihA8">non-native field arithmetic</a> (i.e. computing \(\mathbb{F}_q\) arithmetic in \(\mathbb{F}_p\), with \(q \neq p\)). The representation of each computation would fan out to multiple rows and constraints. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>To understand wire constraints in Plonk more deeply, I recommend the following resources: Vitalik’s <a href="https://vitalik.eth.limo/general/2019/09/22/plonk.html">post</a> on Plonk (see “Copy constraints” section), Aztec’s <a href="https://hackmd.io/@aztec-network/plonk-arithmetiization-air">notes</a> on arithmetization, and Ariel Gabizon’s <a href="https://web.archive.org/web/20221204004349/https://hackmd.io/@arielg/ByFgSDA7D">notes</a> on multiset checks. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>This follows from the <a href="https://en.wikipedia.org/wiki/Schwartz%E2%80%93Zippel_lemma">Schwartz-Zippel Lemma</a>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5">
      <p>The second line follows from the <a href="https://en.wikipedia.org/wiki/Polynomial_remainder_theorem">polynomial remainder theorem</a>. The fourth line follows from the fact that \(\prod_{i=0}^{n-1} (x-\omega^i) = (x^n -1)\). <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6">
      <p>Technically the statements are not logically equivalent. They are probabilistically equivalent (which we denote as “\(\iff \hspace{-2mm}{_p}\)”), since the first step relies on the Schwartz-Zippel Lemma. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7">
      <p>See Dankrad Feist’s <a href="https://dankradfeist.de/ethereum/2020/06/16/kate-polynomial-commitments.html">post</a> or Alin Tomescu’s <a href="https://alinush.github.io/kzg#evaluation-proofs">notes</a> for a refresher on how to generate a KZG evaluation proof. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8">
      <p>This again follows from the Schwartz-Zippel Lemma. <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><summary type="html"><![CDATA[Written by Andy Arditi and Ye Zhang.]]></summary></entry><entry><title type="html">KZG in practice: polynomial commitment schemes and their usage in scaling Ethereum</title><link href="https://www.andyrdt.com/2022/10/25/kzg.html" rel="alternate" type="text/html" title="KZG in practice: polynomial commitment schemes and their usage in scaling Ethereum" /><published>2022-10-25T14:00:00+00:00</published><updated>2022-10-25T14:00:00+00:00</updated><id>https://www.andyrdt.com/2022/10/25/kzg</id><content type="html" xml:base="https://www.andyrdt.com/2022/10/25/kzg.html"><![CDATA[]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry></feed>