Skip to content

zorch.pcs.jagged.commit

SP1 trace commit: stacked RS-encode of a jagged region + SMCS commit.

The dense buffer becomes one [S, K] stacked MLE whose columns are RS-encoded (BitReversedReedSolomon) into a [S*blowup, K] bit-reversed codeword, Merkle-committed via the SMCS, then bound to the region's row/column structure. The commit half of the jagged PCS — it produces the StackedRound the stacked open (zorch.pcs.jagged.open) consumes.

jit=True runs the commit as three @jit zones — the stacked_basefold_open zoning recipe. Only the encode + leaf-hash prologue's shapes carry K (the stacked column count); the Merkle fold's O(depth) compile — the dominant one — keys on the leaf count S*blowup plus the identity-hashed smcs static, so it compiles once per leaf count for each long-lived SingleMatrixCommitmentScheme (the shard prover holds one for its lifetime; a fresh instance recompiles) and is shared by every shard of that height; the root/structure bind tail — the only counts-shaped work, a two-permute graph — recompiles per chip count without ever touching the fold. Byte-identical to eager either way (the zone cuts sit on the leaf-digest and raw-root layers both paths compute).

TraceCommitData dataclass

Prover-side witness the opening stage retains: the [S,K] message mle and the digest tree. The codeword is not kept — the open re-encodes it from mle. The row/column counts are kept because the structure hash bound them and the verifier rebind needs the exact device values.

Source code in zorch/pcs/jagged/commit.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@partial(
    frx.tree_util.register_dataclass,
    data_fields=[
        "dense",
        "mle",
        "digest_layers",
        "row_counts",
        "column_counts",
        "smcs_commitment",
    ],
    meta_fields=[],
)
@dataclass(frozen=True)
class TraceCommitData:
    """Prover-side witness the opening stage retains: the ``[S,K]`` message
    ``mle`` and the digest tree. The codeword is not kept — the open re-encodes
    it from ``mle``. The row/column counts are kept because the structure hash
    bound them and the verifier rebind needs the exact device values."""

    dense: Array
    mle: Array
    digest_layers: list[Array]
    row_counts: Array
    column_counts: Array
    smcs_commitment: Array  # shape-bound SMCS root, before structure binding

commit_region

commit_region(
    region: JaggedRegion,
    smcs: SingleMatrixCommitmentScheme,
    *,
    log_blowup: int,
    jit: bool = False
) -> tuple[Array, TraceCommitData]

Commit a packed region; returns (bound_commitment, prover_data).

jit fuses each zone (the module docstring) — required at rsp scale on a 32 GB device; eager runs the same three bodies un-fused. Byte-identical either way. The ~6 GB blow-up codeword never leaves this function: the open re-encodes it from mle, so it never stays device-resident.

Source code in zorch/pcs/jagged/commit.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def commit_region(
    region: JaggedRegion,
    smcs: SingleMatrixCommitmentScheme,
    *,
    log_blowup: int,
    jit: bool = False,
) -> tuple[Array, TraceCommitData]:
    """Commit a packed region; returns ``(bound_commitment, prover_data)``.

    ``jit`` fuses each zone (the module docstring) — required at rsp scale on a
    32 GB device; eager runs the same three bodies un-fused. Byte-identical either
    way. The ~6 GB blow-up codeword never leaves this function: the open
    re-encodes it from ``mle``, so it never stays device-resident."""
    message = region.block
    row_counts = fnp.array(region.row_counts, dtype=message.dtype)
    column_counts = fnp.array(region.column_counts, dtype=message.dtype)
    K, S = message.shape
    # The [log_height, width] separator preimage as a value, so the fold zone
    # reads K as data rather than a compile key.
    shape_params = fnp.array(
        [log2_strict_usize(S << log_blowup), K], dtype=message.dtype
    )

    mle, leaf_digests = (_prologue_jit if jit else _prologue)(
        smcs, message, log_blowup=log_blowup
    )
    raw_root, digest_layers = (_fold_jit if jit else _fold)(smcs, leaf_digests)
    bound, commitment = (_bind_jit if jit else _bind)(
        smcs, raw_root, shape_params, row_counts, column_counts
    )
    return bound, TraceCommitData(
        dense=region.dense,
        mle=mle,
        digest_layers=digest_layers,
        row_counts=row_counts,
        column_counts=column_counts,
        smcs_commitment=commitment,
    )