LLZK 2.1.1
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
WitgenUtils.cpp
Go to the documentation of this file.
1//===-- WitgenUtils.cpp - llzk-witgen shared helpers ------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
10#include "WitgenUtils.h"
11
12#include "Errors.h"
13
14#include "llzk/Util/Compare.h"
16
17#include <mlir/IR/BuiltinTypes.h>
18
19#include <llvm/ADT/SmallVector.h>
20#include <llvm/ADT/Twine.h>
21
22#include <climits>
23#include <limits>
24#include <random>
25
26using namespace mlir;
27
28namespace llzk::witgen {
29
30static llvm::Expected<size_t>
31dynamicAPIntToSize(const llvm::DynamicAPInt &value, llvm::Twine context) {
32 if (value < 0) {
33 return makeError(context + " would underflow size_t");
34 }
35 llvm::APSInt as = llzk::toAPSInt(value);
36 if (as.getActiveBits() > std::numeric_limits<size_t>::digits) {
37 return makeError(context + " would overflow size_t");
38 }
39 return static_cast<size_t>(as.getZExtValue());
40}
41
42llvm::Expected<size_t>
43checkedDynamicAPIntToSize(const llvm::DynamicAPInt &value, llvm::StringRef context) {
44 return dynamicAPIntToSize(value, context);
45}
46
47llvm::Expected<size_t> checkedShapeDimToSize(int64_t dim, llvm::StringRef context) {
48 if (ShapedType::isDynamic(dim)) {
49 return makeError(llvm::Twine(context) + " requires a static shape");
50 }
51 if (dim < 0) {
52 return makeError(llvm::Twine(context) + " has a negative dimension");
53 }
54 llvm::DynamicAPInt value(dim);
55 return dynamicAPIntToSize(value, llvm::Twine(context) + " dimension");
56}
57
58llvm::Expected<size_t>
59getStaticShapeElementCount(llvm::ArrayRef<int64_t> shape, llvm::StringRef context) {
60 llvm::DynamicAPInt count(1);
61 for (int64_t dim : shape) {
62 auto dimSize = checkedShapeDimToSize(dim, context);
63 if (!dimSize) {
64 return dimSize.takeError();
65 }
66 count *= toDynamicAPInt(*dimSize);
67 }
68 return dynamicAPIntToSize(count, context);
69}
70
71llvm::Expected<size_t> getStaticElementCount(ShapedType type, llvm::StringRef context) {
72 if (!type.hasStaticShape()) {
73 return makeError(llvm::Twine(context) + " requires a static shape");
74 }
75 int64_t count = type.getNumElements();
76 if (count < 0) {
77 return makeError(llvm::Twine(context) + " has an invalid negative element count");
78 }
79 return dynamicAPIntToSize(llvm::DynamicAPInt(count), context);
80}
81
82std::mt19937_64 makeDefaultValueRng(const WitgenOptions &options) {
83 if (options.randomSeed) {
84 return std::mt19937_64(*options.randomSeed);
85 }
86 std::random_device rd;
87 std::seed_seq seed {rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()};
88 return std::mt19937_64(seed);
89}
90
91llvm::DynamicAPInt randomFieldElement(std::mt19937_64 &rng, const Field &field) {
92 const llvm::DynamicAPInt prime = field.prime();
93 if (prime == 0) {
94 return prime;
95 }
96
97 // Use rejection sampling to produce a uniform value in [0, prime).
98 // Generate a random value with the same bit width as the prime; if it is
99 // >= prime, discard and retry. The rejection probability is < 50%, so the
100 // expected number of iterations is less than 2.
101 const unsigned bitWidth = field.bitWidth();
102 const unsigned numWords = (bitWidth + 63U) / 64U;
103 std::uniform_int_distribution<uint64_t> wordDist;
104 llvm::SmallVector<uint64_t, 4> words(numWords);
105 while (true) {
106 for (uint64_t &word : words) {
107 word = wordDist(rng);
108 }
109 // APInt truncates the top word to exactly bitWidth bits, keeping the
110 // candidate in [0, 2^bitWidth).
111 llvm::DynamicAPInt value = toDynamicAPInt(llvm::APInt(bitWidth, words));
112 if (value < prime) {
113 return field.reduce(value);
114 }
115 }
116}
117
118int64_t randomIndexValue(std::mt19937_64 &rng) {
119 return std::uniform_int_distribution<
120 int64_t>(std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max())(rng);
121}
122
123bool randomBoolValue(std::mt19937_64 &rng) {
124 return std::uniform_int_distribution<int>(0, 1)(rng) != 0;
125}
126
127} // namespace llzk::witgen
This file implements helper methods for constructing DynamicAPInts.
Information about the prime finite field used for the interval analysis.
Definition Field.h:36
llvm::DynamicAPInt prime() const
For the prime field p, returns p.
Definition Field.h:72
llvm::DynamicAPInt reduce(const llvm::DynamicAPInt &i) const
Returns i mod p and reduces the result into the appropriate bitwidth.
unsigned bitWidth() const
Definition Field.h:107
std::mt19937_64 makeDefaultValueRng(const WitgenOptions &options)
Seed an RNG for random/default witness value materialization.
llvm::Expected< size_t > getStaticShapeElementCount(llvm::ArrayRef< int64_t > shape, llvm::StringRef context)
Return the static element count for one shape, rejecting dynamic sizes.
llvm::DynamicAPInt randomFieldElement(std::mt19937_64 &rng, const Field &field)
Draw a uniformly distributed field element in [0, prime).
bool randomBoolValue(std::mt19937_64 &rng)
Draw a uniformly distributed boolean value.
llvm::Expected< size_t > checkedDynamicAPIntToSize(const llvm::DynamicAPInt &value, llvm::StringRef context)
Convert a DynamicAPInt into size_t after validating its range.
llvm::Expected< size_t > getStaticElementCount(ShapedType type, llvm::StringRef context)
llvm::Expected< size_t > checkedShapeDimToSize(int64_t dim, llvm::StringRef context)
Convert one static dimension to size_t, rejecting dynamic or invalid sizes.
int64_t randomIndexValue(std::mt19937_64 &rng)
Draw a uniformly distributed signed index value.
llvm::Error makeError(const llvm::Twine &msg)
Build a string-backed error for user-facing witgen failures.
Definition Errors.h:18
DynamicAPInt toDynamicAPInt(StringRef str)
APSInt toAPSInt(const DynamicAPInt &i)
Configure one llzk-witgen execution.
std::optional< uint64_t > randomSeed