Skip to content

Commit

Permalink
Add bindings for r1cs_to_qap. Test SAP/QAP (#48)
Browse files Browse the repository at this point in the history
* Add Binding for r1cs

* Fix Todos for r1cs.cpp

* Add Binding for Fp_model class type and some test

Fp_model class type will be used as class type for relations and used in testing

* Add Test for QAP & SAP, Bindings for r1cs to QAP
gargarchit authored Jun 12, 2020
1 parent d03a99a commit d2c873b
Showing 11 changed files with 252 additions and 13 deletions.
1 change: 0 additions & 1 deletion examples/test.py

This file was deleted.

3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
set(
SRC
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/utils/Fp_model.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/common/data_structures/integer_permutation.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/common/data_structures/set_commitment.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/common/routing_algorithms/benes_routing_algorithm.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/common/routing_algorithms/as_waksman_routing_algorithm.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/common/default_types/r1cs_ppzkpcd_pp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/common/default_types/tinyram_ppzksnark_pp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/relations/constraint_satisfaction_problems/r1cs/r1cs.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/relations/constraint_satisfaction_problems/r1cs/r1cs_examples.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/relations/arithmetic_programs/qap.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/relations/arithmetic_programs/sap.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/reductions/r1cs_to_qap.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PyZPK/binding.cpp"
)

6 changes: 6 additions & 0 deletions src/PyZPK/binding.cpp
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
#include <pybind11/pybind11.h>
namespace py = pybind11;

void init_utils_Fp_model(py::module &);
void init_data_structures_integer_permutation(py::module &);
void init_data_structures_set_commitment(py::module &);
void init_algorithms_benes(py::module &);
void init_algorithms_as_waksman(py::module &);
void init_default_types_r1cs_ppzkpcd_pp(py::module &);
void init_default_types_tinyram_ppzksnark_pp(py::module &);
void init_relations_constraint_satisfaction_problems_r1cs(py::module &);
void init_relations_constraint_satisfaction_problems_r1cs_examples(py::module &);
void init_relations_arithmetic_programs_qap(py::module &);
void init_relations_arithmetic_programs_sap(py::module &);
void init_reductions_r1cs_to_qap(py::module &);

PYBIND11_MODULE(pyzpk, m)
{
m.doc() = "Python wrapper for open source Zero Proof Knowledge Library";

init_utils_Fp_model(m);
init_data_structures_integer_permutation(m);
init_data_structures_set_commitment(m);
init_algorithms_benes(m);
init_algorithms_as_waksman(m);
init_default_types_r1cs_ppzkpcd_pp(m);
init_default_types_tinyram_ppzksnark_pp(m);
init_relations_constraint_satisfaction_problems_r1cs(m);
init_relations_constraint_satisfaction_problems_r1cs_examples(m);
init_relations_arithmetic_programs_qap(m);
init_relations_arithmetic_programs_sap(m);
init_reductions_r1cs_to_qap(m);
}
34 changes: 34 additions & 0 deletions src/PyZPK/reductions/r1cs_to_qap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <pybind11/stl.h>
#include <libff/algebra/curves/mnt/mnt6/mnt6_pp.hpp>
#include <libff/algebra/fields/field_utils.hpp>
#include <libff/algebra/scalar_multiplication/multiexp.hpp>
#include <libff/common/profiling.hpp>
#include <libff/common/utils.hpp>
#include <libsnark/relations/arithmetic_programs/qap/qap.hpp>
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp>
#include <libsnark/reductions/r1cs_to_qap/r1cs_to_qap.hpp>
using namespace std;
namespace py = pybind11;
using namespace libsnark;
using namespace libff;

// Implementation of interfaces for a R1CS-to-QAP reduction.
void init_reductions_r1cs_to_qap(py::module &m)
{
using FieldT = Fp_model<5l, libff::mnt46_modulus_B>;

// Instance map for the R1CS-to-QAP reduction.
m.def("r1cs_to_qap_instance_map", &r1cs_to_qap_instance_map<FieldT>, py::arg("cs"));

// Instance map for the R1CS-to-QAP reduction followed by evaluation of the resulting QAP instance.
m.def("r1cs_to_qap_instance_map_with_evaluation", &r1cs_to_qap_instance_map_with_evaluation<FieldT>,
py::arg("cs"), py::arg("t"));

// Witness map for the R1CS-to-QAP reduction.
// The witness map takes zero knowledge into account when d1,d2,d3 are random.
m.def("r1cs_to_qap_witness_map", &r1cs_to_qap_witness_map<FieldT>,
py::arg("cs"), py::arg("primary_input"), py::arg("auxiliary_input"),
py::arg("d1"), py::arg("d2"), py::arg("d3"));
}
3 changes: 2 additions & 1 deletion src/PyZPK/relations/arithmetic_programs/qap.cpp
Original file line number Diff line number Diff line change
@@ -8,15 +8,16 @@
#include <libff/common/utils.hpp>
#include <libfqfft/evaluation_domain/evaluation_domain.hpp>
#include <libsnark/relations/arithmetic_programs/qap/qap.hpp>
#define FieldT libff::Fr<libff::mnt6_pp>

using namespace std;
namespace py = pybind11;
using namespace libsnark;
using namespace libff;

// Implementation of interfaces for a QAP ("Quadratic Arithmetic Program").
void init_relations_arithmetic_programs_qap(py::module &m)
{
using FieldT = Fp_model<5l, libff::mnt46_modulus_B>;
// A QAP instance.
py::class_<qap_instance<FieldT>>(m, "qap_instance")
.def(py::init<const std::shared_ptr<libfqfft::evaluation_domain<FieldT>> &,
3 changes: 2 additions & 1 deletion src/PyZPK/relations/arithmetic_programs/sap.cpp
Original file line number Diff line number Diff line change
@@ -8,15 +8,16 @@
#include <libff/common/utils.hpp>
#include <libfqfft/evaluation_domain/evaluation_domain.hpp>
#include <libsnark/relations/arithmetic_programs/sap/sap.hpp>
#define FieldT libff::Fr<libff::mnt6_pp>

using namespace std;
namespace py = pybind11;
using namespace libsnark;
using namespace libff;

// Implementation of interfaces for a SAP ("Square Arithmetic Program").
void init_relations_arithmetic_programs_sap(py::module &m)
{
using FieldT = Fp_model<5l, libff::mnt46_modulus_B>;
// A SAP instance.
py::class_<sap_instance<FieldT>>(m, "sap_instance")
.def(py::init<const std::shared_ptr<libfqfft::evaluation_domain<FieldT>> &,
98 changes: 98 additions & 0 deletions src/PyZPK/relations/constraint_satisfaction_problems/r1cs/r1cs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/cast.h>
#include <pybind11/complex.h>
#include <pybind11/functional.h>
#include <pybind11/chrono.h>
#include <vector>
#include <cassert>
#include <libff/algebra/curves/mnt/mnt6/mnt6_pp.hpp>
#include <libff/algebra/fields/field_utils.hpp>
#include <libff/algebra/scalar_multiplication/multiexp.hpp>
#include <libff/common/profiling.hpp>
#include <libff/common/utils.hpp>
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp>

using namespace std;
namespace py = pybind11;
using namespace libsnark;
using namespace libff;

// Interfaces for a R1CS constraint, a R1CS variable assignment, and a R1CS constraint system.
void init_relations_constraint_satisfaction_problems_r1cs(py::module &m)
{
using FieldT = Fp_model<5l, libff::mnt46_modulus_B>;
py::class_<r1cs_constraint<FieldT>>(m, "r1cs_constraint")
.def(py::init<>())
.def_readwrite("a", &r1cs_constraint<FieldT>::a)
.def_readwrite("b", &r1cs_constraint<FieldT>::b)
.def_readwrite("c", &r1cs_constraint<FieldT>::c)
.def(py::init<const linear_combination<FieldT> &,
const linear_combination<FieldT> &,
const linear_combination<FieldT> &>())
.def(py::init<const std::initializer_list<linear_combination<FieldT>> &,
const std::initializer_list<linear_combination<FieldT>> &,
const std::initializer_list<linear_combination<FieldT>> &>())
.def(
"__eq__", [](r1cs_constraint<FieldT> const &self, r1cs_constraint<FieldT> const &other) { return self == other; }, py::is_operator())
.def("__ostr__", [](r1cs_constraint<FieldT> const &self) {
std::ostringstream os;
os << self.a << "\n";
os << self.b << "\n";
os << self.c << "\n";
return os;
})
.def("__istr__", [](r1cs_constraint<FieldT> &self) {
std::istringstream os;
os >> self.a;
os >> self.b;
os >> self.c;
return os;
});

py::class_<r1cs_constraint_system<FieldT>>(m, "r1cs_constraint_system")
.def(py::init<>())
.def_readwrite("primary_input_size", &r1cs_constraint_system<FieldT>::primary_input_size)
.def_readwrite("auxiliary_input_size", &r1cs_constraint_system<FieldT>::auxiliary_input_size)
.def_readwrite("constraints", &r1cs_constraint_system<FieldT>::constraints)
.def("num_inputs", &r1cs_constraint_system<FieldT>::num_inputs)
.def("num_variables", &r1cs_constraint_system<FieldT>::num_variables)
.def("num_constraints", &r1cs_constraint_system<FieldT>::num_constraints)
.def("is_valid", &r1cs_constraint_system<FieldT>::is_valid)
.def("add_constraint", (void (r1cs_constraint_system<FieldT>::*)(const r1cs_constraint<FieldT> &)) & r1cs_constraint_system<FieldT>::add_constraint, py::arg("c"))
.def("add_constraint", (void (r1cs_constraint_system<FieldT>::*)(const r1cs_constraint<FieldT> &, const std::string &)) & r1cs_constraint_system<FieldT>::add_constraint, py::arg("c"), py::arg("annotation"))
.def("is_satisfied", &r1cs_constraint_system<FieldT>::is_satisfied, py::arg("primary_input"), py::arg("auxiliary_input"))
.def("swap_AB_if_beneficial", &r1cs_constraint_system<FieldT>::swap_AB_if_beneficial)
.def(
"__eq__", [](r1cs_constraint_system<FieldT> const &self, r1cs_constraint_system<FieldT> const &other) { return self == other; }, py::is_operator())
.def("report_linear_constraint_statistics", &r1cs_constraint_system<FieldT>::report_linear_constraint_statistics)
.def("__ostr__", [](r1cs_constraint_system<FieldT> const &self) {
std::ostringstream os;
os << self.primary_input_size << "\n";
os << self.auxiliary_input_size << "\n";
for (const r1cs_constraint<FieldT> &c : self.constraints)
{
os << c;
}
return os;
})
.def("__istr__", [](r1cs_constraint_system<FieldT> &self) {
std::istringstream os;
os >> self.primary_input_size;
os >> self.auxiliary_input_size;
self.constraints.clear();
size_t s;
os >> s;
char b;
os.read(&b, 1);
self.constraints.reserve(s);
for (size_t i = 0; i < s; ++i)
{
r1cs_constraint<FieldT> c;
os >> c;
self.constraints.emplace_back(c);
}
return os;
});
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/cast.h>
#include <pybind11/complex.h>
#include <vector>
#include <cassert>
#include <libff/algebra/curves/mnt/mnt6/mnt6_pp.hpp>
#include <libff/algebra/fields/field_utils.hpp>
#include <libff/algebra/scalar_multiplication/multiexp.hpp>
#include <libff/common/profiling.hpp>
#include <libff/common/utils.hpp>
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp>
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp>
#define FieldT libff::Fr<libff::mnt6_pp>

using namespace std;
namespace py = pybind11;
using namespace libsnark;
using namespace libff;

// Declaration of interfaces for a R1CS example, as well as functions to sample
// R1CS examples with prescribed parameters (according to some distribution).
// R1CS examples with prescribed parameters (according to some distribution)
void init_relations_constraint_satisfaction_problems_r1cs_examples(py::module &m)
{
py::class_<r1cs_constraint_system<FieldT>>(m, "r1cs_constraint_system")
.def(py::init<>())
.def("is_satisfied", &r1cs_constraint_system<FieldT>::is_satisfied);

using FieldT = Fp_model<5l, libff::mnt46_modulus_B>;
py::class_<r1cs_example<FieldT>>(m, "r1cs_example")
.def(py::init<const r1cs_constraint_system<FieldT> &,
const r1cs_primary_input<FieldT> &,
const r1cs_auxiliary_input<FieldT> &>())
.def_readwrite("constraint_system", &r1cs_example<FieldT>::constraint_system)
.def_readwrite("primary_input", &r1cs_example<FieldT>::primary_input)
.def_readwrite("auxiliary_input", &r1cs_example<FieldT>::auxiliary_input);

m.def("generate_r1cs_example_with_field_input", &generate_r1cs_example_with_field_input<FieldT>, py::arg("num_constraints"), py::arg("num_inputs"));
m.def("generate_r1cs_example_with_binary_input", &generate_r1cs_example_with_binary_input<FieldT>, py::arg("num_constraints"), py::arg("num_inputs"));
}
}
56 changes: 56 additions & 0 deletions src/PyZPK/utils/Fp_model.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/cast.h>
#include <pybind11/complex.h>
#include <vector>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <random>
#include <cstddef>
#include <iostream>
#include <libff/algebra/scalar_multiplication/multiexp.hpp>
#include <libff/algebra/curves/mnt/mnt6/mnt6_pp.hpp>
#include <libff/algebra/curves/mnt/mnt6/mnt6_g1.hpp>
#include <libff/algebra/curves/mnt/mnt6/mnt6_g2.hpp>
#include <libff/algebra/curves/mnt/mnt6/mnt6_init.hpp>
#include <libff/algebra/curves/mnt/mnt6/mnt6_pairing.hpp>
#include <libff/algebra/curves/public_params.hpp>
#include <libff/algebra/exponentiation/exponentiation.hpp>
#include <libff/algebra/fields/field_utils.hpp>
#include <libff/algebra/fields/bigint.hpp>
#include <libff/algebra/fields/fp_aux.tcc>
#include <libff/common/profiling.hpp>
#include <libff/common/utils.hpp>
#include <libff/common/serialization.hpp>
#include <gmp.h>

using namespace std;
namespace py = pybind11;
using namespace libff;

// Used as FieldT class type
void init_utils_Fp_model(py::module &m)
{
// bigint wrapper class around GMP's MPZ long integers.
py::class_<bigint<5l>>(m, "bigint")
.def(py::init<>())
.def(py::init<const unsigned long>())
.def(py::init<const char*>())
.def("test_bit", &bigint<5l>::test_bit)
.def("randomize", &bigint<5l>::randomize);

py::class_<mnt6_pp>(m, "mnt6_pp");

// Implementation of arithmetic in the finite field F[p], for prime p of fixed length.
py::class_<Fp_model<5l, libff::mnt46_modulus_B>>(m, "Fp_model")
.def(py::init<>())
.def(py::init<const bigint<5l> &>())
.def(py::init<const long, const bool>())
.def_readwrite("mont_repr", &Fp_model<5l, libff::mnt46_modulus_B>::mont_repr)
.def("random_element", &Fp_model<5l, libff::mnt46_modulus_B>::random_element) // Todo
.def("inverse", &Fp_model<5l, libff::mnt46_modulus_B>::inverse);

}
43 changes: 43 additions & 0 deletions test/test_relations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import pytest
import pyzpk
import math

# Test for QAP
@pytest.mark.parametrize("qap_degree, num_inputs, binary_input",
[
# basic_domain_size
(1 << pyzpk.mnt6_Fr_s, 10, True),
# step_domain_size
((1 << 10) + (1 << 8), 10, True),
# extended_domain_size
(1 << (pyzpk.mnt6_Fr_s+1), 10, True),
# extended_domain_size_special
((1 << (pyzpk.mnt6_Fr_s+1))-1, 10, True)
])
def test_qap(qap_degree, num_inputs, binary_input):
assert num_inputs + 1 <= qap_degree
num_constraints = qap_degree - num_inputs - 1
# For Binary input
example = pyzpk.generate_r1cs_example_with_binary_input(
num_constraints, num_inputs)
assert example.constraint_system.is_satisfied(
example.primary_input, example.auxiliary_input)

# Test for SAP
@pytest.mark.parametrize("sap_degree, num_inputs, binary_input",
[
# basic_domain_size_special
((1 << pyzpk.mnt6_Fr_s) - 1, 10, True),
# # step_domain_size_special
((1 << 10) + (1 << 8) - 1, 10, True),
# # extended_domain_size_special
(1 << (pyzpk.mnt6_Fr_s+1) - 1, 10, True)
])
def test_sap(sap_degree, num_inputs, binary_input):
num_constraints = int((sap_degree - 1) / 2) - num_inputs
assert num_constraints >= 1
# For Binary input
example = pyzpk.generate_r1cs_example_with_binary_input(
num_constraints, num_inputs)
assert example.constraint_system.is_satisfied(
example.primary_input, example.auxiliary_input)
2 changes: 1 addition & 1 deletion test/test_binding.py → test/test_routing_algorithms.py
Original file line number Diff line number Diff line change
@@ -59,4 +59,4 @@ def test_data_structures():
# set a number above max_element for idx 5
vec.set(5,10)
# Check is_valid permute
assert vec.is_valid() == False
assert vec.is_valid() == False

0 comments on commit d2c873b

Please sign in to comment.