Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit f07e93b

Browse files
committed
Added power optimizations #36
1 parent b8fb56d commit f07e93b

File tree

3 files changed

+139
-18
lines changed

3 files changed

+139
-18
lines changed

include/nil/blueprint/transpiler/evm_verifier_gen.hpp

+99-18
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <nil/blueprint/transpiler/templates/commitment_scheme.hpp>
4141
#include <nil/blueprint/transpiler/templates/external_gate.hpp>
4242
#include <nil/blueprint/transpiler/templates/external_lookup.hpp>
43+
#include <nil/blueprint/transpiler/templates/utils_template.hpp>
4344
#include <nil/blueprint/transpiler/lpc_scheme_gen.hpp>
4445
#include <nil/blueprint/transpiler/util.hpp>
4546

@@ -134,6 +135,7 @@ namespace nil {
134135
return result.str();
135136
}
136137

138+
/* Detect whether combination is a polynomial over one variable */
137139
bool detect_polynomial(crypto3::math::non_linear_combination<variable_type> const& comb) {
138140
std::unordered_set<variable_type> comb_vars;
139141

@@ -147,7 +149,32 @@ namespace nil {
147149
return comb_vars.size() == 1;
148150
}
149151

150-
std::string constraint_computation_code_optimize_polynomial(
152+
/* Detect whether term is a power of one variable. If such, return this power */
153+
std::size_t term_is_power(crypto3::math::term<variable_type> const& term) {
154+
const auto &vars = term.get_vars();
155+
auto var = std::cbegin(vars);
156+
157+
if (var == std::cend(vars))
158+
return 0;
159+
160+
variable_type prev_var = *var;
161+
++var;
162+
163+
std::size_t power = 1;
164+
165+
while (var != std::cend(vars)) {
166+
if (*var != prev_var) {
167+
return 0;
168+
}
169+
++power;
170+
prev_var = *var;
171+
++var;
172+
}
173+
174+
return power;
175+
}
176+
177+
std::string constraint_computation_code_optimized(
151178
variable_indices_type &_var_indices,
152179
const constraint_type &constraint
153180
){
@@ -189,20 +216,31 @@ namespace nil {
189216
result << "\t\t/* End using Horner's formula */" << std::endl;
190217
} else {
191218
result << "\t\tsum = 0;" << std::endl;
192-
for( auto it = std::cbegin(comb); it != std::cend(comb); ++it ){
193-
bool coeff_one = (it->get_coeff() == PlaceholderParams::field_type::value_type::one());
194-
if(!coeff_one) result << "\t\tprod = " << it->get_coeff() << ";" << std::endl;
195-
const auto &vars = it->get_vars();
196-
for( auto it2 = std::cbegin(vars); it2 != std::cend(vars); it2++ ){
197-
const variable_type &v = *it2;
198-
if(coeff_one){
199-
coeff_one = false;
200-
result << "\t\tprod = basic_marshalling.get_uint256_be(blob, " << _var_indices.at(v) * 0x20 << ");" << std::endl;
201-
} else{
202-
result << "\t\tprod = mulmod(prod, basic_marshalling.get_uint256_be(blob, " << _var_indices.at(v) * 0x20 << "), modulus);" << std::endl;
219+
for (auto term = std::cbegin(comb); term != std::cend(comb); ++term) {
220+
const auto &vars = term->get_vars();
221+
std::size_t power;
222+
223+
/* Using special powX function is only feasible for powers >= 4 */
224+
if ( _optimize_powers && ((power = term_is_power(*term)) >= 4) ) {
225+
_term_powers.insert(power);
226+
result << "\t\tprod = utils.pow" << power << "(basic_marshalling.get_uint256_be(blob, " << _var_indices.at(vars[0]) * 0x20 << "));" << std::endl;
227+
} else {
228+
for (auto var = std::cbegin(vars); var != std::cend(vars); ++var) {
229+
if (var == std::cbegin(vars)) {
230+
result << "\t\tprod = basic_marshalling.get_uint256_be(blob, " << _var_indices.at(*var) * 0x20 << ");" << std::endl;
231+
} else {
232+
result << "\t\tprod = mulmod(prod, basic_marshalling.get_uint256_be(blob, " << _var_indices.at(*var) * 0x20 << "), modulus);" << std::endl;
233+
}
203234
}
204235
}
205-
result << "\t\tsum = addmod(sum, prod, modulus);" << std::endl;
236+
if (vars.size() == 0) {
237+
result << "\t\tsum = addmod(sum, " << term->get_coeff() << ", modulus);" << std::endl;
238+
} else {
239+
if (term->get_coeff() != PlaceholderParams::field_type::value_type::one()) {
240+
result << "\t\tprod = mulmod(prod, " << term->get_coeff() << ", modulus);" << std::endl;
241+
}
242+
result << "\t\tsum = addmod(sum, prod, modulus);" << std::endl;
243+
}
206244
}
207245
}
208246
return result.str();
@@ -242,11 +280,12 @@ namespace nil {
242280
const typename PlaceholderParams::commitment_scheme_type &lpc_scheme,
243281
std::size_t permutation_size,
244282
std::string folder_name,
245-
std::size_t gates_library_size_threshold = 1600,
246-
std::size_t lookups_library_size_threshold = 1600,
283+
std::size_t gates_library_size_threshold = 1400,
284+
std::size_t lookups_library_size_threshold = 1400,
247285
std::size_t gates_contract_size_threshold = 1400,
248286
std::size_t lookups_contract_size_threshold = 1400,
249-
bool deduce_horner = true
287+
bool deduce_horner = true,
288+
bool optimize_powers = true
250289
) :
251290
_constraint_system(constraint_system),
252291
_common_data(common_data),
@@ -257,7 +296,8 @@ namespace nil {
257296
_lookups_library_size_threshold(lookups_library_size_threshold),
258297
_gates_contract_size_threshold(gates_contract_size_threshold),
259298
_lookups_contract_size_threshold(lookups_contract_size_threshold),
260-
_deduce_horner(deduce_horner)
299+
_deduce_horner(deduce_horner),
300+
_optimize_powers(optimize_powers)
261301
{
262302
std::size_t found = folder_name.rfind("/");
263303
if( found == std::string::npos ){
@@ -346,9 +386,10 @@ namespace nil {
346386
out << "\t\tgate = 0;" << std::endl;
347387
int c = 0;
348388
for(const auto &constraint: gate.constraints){
349-
out << constraint_computation_code_optimize_polynomial(_var_indices, constraint);
389+
out << constraint_computation_code_optimized(_var_indices, constraint);
350390
out << "\t\tgate = addmod(gate, mulmod(theta_acc, sum, modulus), modulus);" << std::endl;
351391
out << "\t\ttheta_acc = mulmod(theta_acc, theta, modulus);" << std::endl;
392+
c++;
352393
}
353394
variable_type sel_var(gate.selector_index, 0, true, variable_type::column_type::selector);
354395
out << "\t\tgate = mulmod(gate, basic_marshalling.get_uint256_be(blob, " << _var_indices.at(sel_var) * 0x20 << "), modulus);" << std::endl;
@@ -427,6 +468,29 @@ namespace nil {
427468
return buckets;
428469
}
429470

471+
std::string generate_power_function(std::size_t power) {
472+
std::stringstream result;
473+
std::vector<std::string> ops;
474+
475+
result << "\tfunction pow" << power << "(uint256 base) internal pure returns (uint256 result) {" << std::endl;
476+
result << "\t\tresult = base;" << std::endl;
477+
478+
while (power > 1) {
479+
if (power & 1) {
480+
ops.push_back("\t\tresult = mulmod(result, base, modulus);");
481+
}
482+
ops.push_back("\t\tresult = mulmod(result, result, modulus);");
483+
power >>= 1;
484+
}
485+
486+
for(auto op = ops.rbegin(); op != ops.rend(); ++op) {
487+
result << *op << std::endl;
488+
}
489+
490+
result << "\t}" << std::endl;
491+
return result.str();
492+
}
493+
430494
std::string print_gate_argument(){
431495
std::size_t gates_count = _constraint_system.gates().size();
432496
if (gates_count == 0)
@@ -479,6 +543,19 @@ namespace nil {
479543
print_gates_library_file(lib.first, lib.second, gate_codes);
480544
}
481545

546+
std::stringstream power_functions;
547+
for(std::size_t power: _term_powers) {
548+
power_functions << generate_power_function(power);
549+
}
550+
551+
std::string utils_library(utils_library_template);
552+
boost::replace_all(utils_library, "$MODULUS$", to_string(PlaceholderParams::field_type::modulus));
553+
boost::replace_all(utils_library, "$POWER_FUNCTIONS$", power_functions.str());
554+
std::ofstream utils;
555+
utils.open(_folder_name + "/utils.sol");
556+
utils << utils_library;
557+
utils.close();
558+
482559
if (inlined_gate_codes.size() > 0) {
483560
gate_argument_str << "\t\tuint256 sum;" << std::endl;
484561
gate_argument_str << "\t\tuint256 prod;" << std::endl;
@@ -708,6 +785,10 @@ namespace nil {
708785
variable_indices_type _var_indices;
709786

710787
bool _deduce_horner;
788+
789+
bool _optimize_powers;
790+
std::unordered_set<std::size_t> _term_powers;
791+
711792
std::string _gate_includes;
712793
std::string _lookup_includes;
713794
std::size_t _gates_contract_size_threshold;

include/nil/blueprint/transpiler/templates/external_gate.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ namespace nil {
4141
pragma solidity >=0.8.4;
4242
4343
import "../../../contracts/basic_marshalling.sol";
44+
import "./utils.sol";
4445
4546
library gate_$TEST_NAME$_$GATE_LIB_ID${
4647
uint256 constant modulus = $MODULUS$;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#ifndef __MODULAR_UTILS_TEMPLATE_HPP__
2+
#define __MODULAR_UTILS_TEMPLATE_HPP__
3+
4+
#include <string>
5+
6+
namespace nil {
7+
namespace blueprint {
8+
std::string utils_library_template = R"(
9+
// SPDX-License-Identifier: Apache-2.0.
10+
//---------------------------------------------------------------------------//
11+
// Copyright (c) 2023 Generated by ZKLLVM-transpiler
12+
//
13+
// Licensed under the Apache License, Version 2.0 (the "License");
14+
// you may not use this file except in compliance with the License.
15+
// You may obtain a copy of the License at
16+
//
17+
// http://www.apache.org/licenses/LICENSE-2.0
18+
//
19+
// Unless required by applicable law or agreed to in writing, software
20+
// distributed under the License is distributed on an "AS IS" BASIS,
21+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22+
// See the License for the specific language governing permissions and
23+
// limitations under the License.
24+
//---------------------------------------------------------------------------//
25+
pragma solidity >=0.8.4;
26+
27+
import "hardhat/console.sol";
28+
29+
library utils {
30+
uint256 constant modulus = $MODULUS$;
31+
32+
$POWER_FUNCTIONS$
33+
34+
}
35+
)";
36+
}
37+
}
38+
39+
#endif //__MODULAR_CONTRACT_TEMPLATE_HPP__

0 commit comments

Comments
 (0)