LLZK 3.0.0
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 <limits>
23#include <random>
24
25using namespace mlir;
26
27namespace llzk::witgen {
28
29llvm::Expected<size_t> checkedShapeDimToSize(int64_t dim, llvm::StringRef context) {
30 if (ShapedType::isDynamic(dim)) {
31 return makeError(llvm::Twine(context) + " requires a static shape");
32 }
33 if (dim < 0) {
34 return makeError(llvm::Twine(context) + " has a negative dimension");
35 }
36 llvm::DynamicAPInt value(dim);
37 return checkedToSize(value, llvm::Twine(context) + " dimension");
38}
39
40llvm::Expected<size_t>
41getStaticShapeElementCount(llvm::ArrayRef<int64_t> shape, llvm::StringRef context) {
42 llvm::DynamicAPInt count(1);
43 for (int64_t dim : shape) {
44 auto dimSize = checkedShapeDimToSize(dim, context);
45 if (!dimSize) {
46 return dimSize.takeError();
47 }
48 count *= dim;
49 }
50 return checkedToSize(count, context);
51}
52
53llvm::Expected<size_t> getStaticElementCount(ShapedType type, llvm::StringRef context) {
54 if (!type.hasStaticShape()) {
55 return makeError(llvm::Twine(context) + " requires a static shape");
56 }
57 int64_t count = type.getNumElements();
58 if (count < 0) {
59 return makeError(llvm::Twine(context) + " has an invalid negative element count");
60 }
61 return checkedToSize(llvm::DynamicAPInt(count), context);
62}
63
64std::mt19937_64 makeDefaultValueRng(const WitgenOptions &options) {
65 if (options.randomSeed) {
66 return std::mt19937_64(*options.randomSeed);
67 }
68 std::random_device rd;
69 std::seed_seq seed {rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()};
70 return std::mt19937_64(seed);
71}
72
73llvm::DynamicAPInt randomFieldElement(std::mt19937_64 &rng, const Field &field) {
74 const llvm::DynamicAPInt prime = field.prime();
75 if (prime == 0) {
76 return prime;
77 }
78
79 // Use rejection sampling to produce a uniform value in [0, prime).
80 // Generate a random value with the same bit width as the prime; if it is
81 // >= prime, discard and retry. The rejection probability is < 50%, so the
82 // expected number of iterations is less than 2.
83 const unsigned bitWidth = field.bitWidth();
84 const unsigned numWords = (bitWidth + 63U) / 64U;
85 std::uniform_int_distribution<uint64_t> wordDist;
86 llvm::SmallVector<uint64_t, 4> words(numWords);
87 while (true) {
88 for (uint64_t &word : words) {
89 word = wordDist(rng);
90 }
91 // APInt truncates the top word to exactly bitWidth bits, keeping the
92 // candidate in [0, 2^bitWidth).
93 llvm::DynamicAPInt value = toDynamicAPInt(llvm::APInt(bitWidth, words));
94 if (value < prime) {
95 return field.reduce(value);
96 }
97 }
98}
99
100int64_t randomIndexValue(std::mt19937_64 &rng) {
101 return std::uniform_int_distribution<
102 int64_t>(std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max())(rng);
103}
104
105bool randomBoolValue(std::mt19937_64 &rng) {
106 return std::uniform_int_distribution<int>(0, 1)(rng) != 0;
107}
108
109} // namespace llzk::witgen
This file implements helpers for DynamicAPInt operations and conversions that LLVM does not provide.
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:70
llvm::DynamicAPInt reduce(const llvm::DynamicAPInt &i) const
Returns the canonical representative of i mod p in [0, p).
unsigned bitWidth() const
Width sufficient for the modulus and its representatives, independent of literal storage.
Definition Field.h:103
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 > 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
Expected< size_t > checkedToSize(const DynamicAPInt &value, const Twine &context)
DynamicAPInt toDynamicAPInt(StringRef str)
Configure one llzk-witgen execution.
std::optional< uint64_t > randomSeed