Circom किसी लूप में components को सीधे instantiate करने की अनुमति नहीं देता है। उदाहरण के लिए, निम्नलिखित कोड को compile करने पर नीचे दी गई error आती है।
include "./node_modules/circomlib/circuits/comparators.circom";
template IsSorted(n) {
signal input in[n];
for (var i = 0; i < n; i++) {
component lt = LessEqThan(252); // error here
lt.in[0] <== in[0];
lt.in[1] <== in[1];
lt.out === 1;
}
}
component main = IsSorted(8);
Signal or component declaration inside While scope. Signal and component can only be defined in the initial scope or in If scopes with known condition
इसका समाधान (workaround) यह है कि components का एक array declare किया जाए लेकिन component type को specify न किया जाए:
pragma circom 2.1.8;
include "./node_modules/circomlib/circuits/comparators.circom";
template IsSorted(n) {
signal input in[n];
// declare array of components
// but do not specify the component type
component lessThan[n];
for (var i = 0; i < n - 1; i++) {
lessThan[i] = LessEqThan(252); // specify type in the loop
lessThan[i].in[0] <== in[i];
lessThan[i].in[1] <== in[i+1];
lessThan[i].out === 1;
}
}
component main = IsSorted(8);
जब components को इस तरीके से declare किया जाता है, तो किसी signal के लिए नीचे दिखाए गए अनुसार “one-line assignment” करना संभव नहीं होता है:
pragma circom 2.1.8;
include "./node_modules/circomlib/circuits/comparators.circom";
template IsSorted() {
signal input in[4];
signal leq1;
signal leq2;
signal leq3;
// one line assignment to the signal
leq1 <== LessEqThan(252)([in[0], in[1]]);
leq2 <== LessEqThan(252)([in[1], in[2]]);
leq3 <== LessEqThan(252)([in[2], in[3]]);
leq1 === 1;
leq2 === 1;
leq3 === 1;
}
component main = IsSorted();
एक लूप के बाहर, signals को एक ही लाइन पर सेट किया जा सकता है। हालाँकि, लूप के अंदर, हमें assignment को अधिक steps में लिखना पड़ता है, जैसे हमने lessThan[i] = LessEqThan(252); // specify type in the loop में किया था।
उदाहरण 1: array का max
लूप में components को declare करने के एक उपयोगी उदाहरण को स्पष्ट करने के लिए, हम यह दिखाते हैं कि यह कैसे साबित किया जाए कि k किसी array का maximum है। ऐसा करने के लिए, हमें यह constrain करना होगा कि k हर दूसरे element से बड़ा या उसके बराबर है और यह कम से कम एक element के बराबर है। यह समझने के लिए कि equality check क्यों आवश्यक है, विचार करें कि 18, [7, 8, 15] के सभी elements से बड़ा या उनके बराबर है, लेकिन यह array का maximum नहीं है।
निम्नलिखित Circom कोड constraints जनरेट किए बिना array की maximum वैल्यू को compute करता है। फिर, यह n GreaterEqualThan components को चलाता है ताकि यह constrain किया जा सके कि प्रस्तावित max वैल्यू वास्तव में maximum वैल्यू है, और यह भी चेक करता है कि IsEqual components के एक array का उपयोग करके कम से कम एक element k के बराबर है।
include "./node_modules/circomlib/circuits/comparators.circom";
template Max(n) {
signal input in[n];
signal output out;
// no constraints here, just a computation
// to find the max
var max = 0;
for (var i = 0; i < n; i++) {
max = in[i] > max ? in[i] : max;
}
out <-- max;
// for each element in the array, assert that
// max ≥ that element
component GTE[n];
component EQ[n];
var acc;
for (var i = 0; i < n; i++) {
GTE[i] = GreaterEqThan(252);
GTE[i].in[0] <== out;
GTE[i].in[1] <== in[i];
GTE[i].out === 1;
// this is used in the
// next code block to ensure
// that out equals at
// least one of the inputs
EQ[i] = IsEqual();
EQ[i].in[0] <== out;
EQ[i].in[1] <== in[i];
// acc is greater than zero
// (acc != 0) if EQ[i].out
// equals 1 at least one time
acc += EQ[i].out;
}
// assert that out is
// equal to at least one of the
// inputs. if acc = 0 then
// none of the inputs equals
// out
signal allZero;
allZero <== IsEqual()([0, acc]);
allZero === 0;
}
component main = Max(8);
Exercise: एक ऐसा circuit बनाएं जो ऊपर दिए गए कार्य को ही करे, लेकिन min के लिए।
उदाहरण 2: array sorted है
हम यह assert कर सकते हैं कि एक array sorted है, यह चेक करके कि प्रत्येक element अपने बाद वाले element से छोटा या उसके बराबर है। पिछले उदाहरण के विपरीत, जिसमें n components की आवश्यकता थी, हमें n - 1 components की आवश्यकता है क्योंकि हम पड़ोसी वैल्यूज़ की एक-दूसरे से तुलना कर रहे हैं। चूंकि हमारे पास n elements हैं, इसलिए हम n - 1 comparisons करने जा रहे हैं।
यहाँ एक template है जो एक input array in[n] को sorted होने के लिए constrain करता है। ध्यान दें कि यदि किसी array में केवल एक ही element है, तो वह परिभाषा के अनुसार sorted है, और नीचे दिया गया circuit भी उस परिदृश्य के अनुकूल है:
pragma circom 2.1.6;
include "circomlib/comparators.circom";
template IsSorted(n) {
signal input in[n];
component lt[n - 1];
// loop goes up to n - 1, not n
for (var i = 0; i < n - 1; i++) {
lt[i] = LessThan(252);
lt[i].in[0] <== in[i];
lt[i].in[1] <== in[i+1];
lt[i].out === 1;
}
}
component main = IsSorted(3);
उदाहरण 3: सभी items unique हैं
यह चेक करने के लिए कि list में सभी items unique हैं, सबसे सीधा तरीका hashmap का उपयोग करना है — लेकिन arithmetic circuits में hashmaps उपलब्ध नहीं होते हैं। दूसरा सबसे प्रभावी तरीका list को sort करना है, लेकिन एक circuit के अंदर sorting करना काफी मुश्किल है, इसलिए हम अभी के लिए इससे बचते हैं। इसके बाद हमारे पास हर element की हर दूसरे element से तुलना करने का brute force समाधान बचता है। इसके लिए एक nested for-loop की आवश्यकता होती है।
हम जो computation कर रहे हैं, उसे इस प्रकार दर्शाया जा सकता है:
सामान्य तौर पर, यहाँ
inequality checks होंगे, इसलिए हमें उतने ही components की आवश्यकता होगी।
हम नीचे दिखाते हैं कि इसे कैसे पूरा किया जाए:
pragma circom 2.1.8;
include "./node_modules/circomlib/comparators.circom";
template ForceNotEqual() {
signal input in[2];
component iseq = IsEqual();
iseq.in[0] <== in[0];
iseq.in[1] <== in[1];
iseq.out === 0;
}
template AllUnique (n) {
signal input in[n];
// the nested loop below will run
// n * (n - 1) / 2 times
component Fneq[n * (n-1)/2];
// loop from 0 to n - 1
var index = 0;
for (var i = 0; i < n - 1; i++) {
// loop from i + 1 to n
for (var j = i + 1; j < n; j++) {
Fneq[index] = ForceNotEqual();
Fneq[index].in[0] <== in[i];
Fneq[index].in[1] <== in[j];
index++;
}
}
}
component main = AllUnique(5);
Summary
एक लूप के अंदर Circom components का उपयोग करने के लिए, हम type को specify किए बिना लूप के बाहर components का एक array declare करते हैं।
फिर लूप के अंदर, हम components को declare करते हैं और component के inputs और outputs को constrain करते हैं।