Squashed 'src/snark/' content from commit 9ada3f8

git-subtree-dir: src/snark
git-subtree-split: 9ada3f84ab484c57b2247c2f41091fd6a0916573
This commit is contained in:
Jack Grigg
2017-08-02 11:17:25 +01:00
commit 51e448641d
123 changed files with 22264 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
/** @file
*****************************************************************************
Declaration of interfaces for the "basic radix-2" evaluation domain.
Roughly, the domain has size m = 2^k and consists of the m-th roots of unity.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_HPP_
#define BASIC_RADIX2_DOMAIN_HPP_
#include "algebra/evaluation_domain/evaluation_domain.hpp"
namespace libsnark {
template<typename FieldT>
class basic_radix2_domain : public evaluation_domain<FieldT> {
public:
FieldT omega;
basic_radix2_domain(const size_t m);
void FFT(std::vector<FieldT> &a);
void iFFT(std::vector<FieldT> &a);
void cosetFFT(std::vector<FieldT> &a, const FieldT &g);
void icosetFFT(std::vector<FieldT> &a, const FieldT &g);
std::vector<FieldT> lagrange_coeffs(const FieldT &t);
FieldT get_element(const size_t idx);
FieldT compute_Z(const FieldT &t);
void add_poly_Z(const FieldT &coeff, std::vector<FieldT> &H);
void divide_by_Z_on_coset(std::vector<FieldT> &P);
};
} // libsnark
#include "algebra/evaluation_domain/domains/basic_radix2_domain.tcc"
#endif // BASIC_RADIX2_DOMAIN_HPP_

View File

@@ -0,0 +1,112 @@
/** @file
*****************************************************************************
Implementation of interfaces for the "basic radix-2" evaluation domain.
See basic_radix2_domain.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_TCC_
#define BASIC_RADIX2_DOMAIN_TCC_
#include "algebra/evaluation_domain/domains/basic_radix2_domain_aux.hpp"
namespace libsnark {
template<typename FieldT>
basic_radix2_domain<FieldT>::basic_radix2_domain(const size_t m) : evaluation_domain<FieldT>(m)
{
assert(m > 1);
const size_t logm = log2(m);
assert(logm <= (FieldT::s));
omega = get_root_of_unity<FieldT>(m);
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::FFT(std::vector<FieldT> &a)
{
enter_block("Execute FFT");
assert(a.size() == this->m);
_basic_radix2_FFT(a, omega);
leave_block("Execute FFT");
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::iFFT(std::vector<FieldT> &a)
{
enter_block("Execute inverse FFT");
assert(a.size() == this->m);
_basic_radix2_FFT(a, omega.inverse());
const FieldT sconst = FieldT(a.size()).inverse();
for (size_t i = 0; i < a.size(); ++i)
{
a[i] *= sconst;
}
leave_block("Execute inverse FFT");
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::cosetFFT(std::vector<FieldT> &a, const FieldT &g)
{
enter_block("Execute coset FFT");
_multiply_by_coset(a, g);
FFT(a);
leave_block("Execute coset FFT");
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::icosetFFT(std::vector<FieldT> &a, const FieldT &g)
{
enter_block("Execute inverse coset IFFT");
iFFT(a);
_multiply_by_coset(a, g.inverse());
leave_block("Execute inverse coset IFFT");
}
template<typename FieldT>
std::vector<FieldT> basic_radix2_domain<FieldT>::lagrange_coeffs(const FieldT &t)
{
return _basic_radix2_lagrange_coeffs(this->m, t);
}
template<typename FieldT>
FieldT basic_radix2_domain<FieldT>::get_element(const size_t idx)
{
return omega^idx;
}
template<typename FieldT>
FieldT basic_radix2_domain<FieldT>::compute_Z(const FieldT &t)
{
return (t^this->m) - FieldT::one();
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::add_poly_Z(const FieldT &coeff, std::vector<FieldT> &H)
{
assert(H.size() == this->m+1);
H[this->m] += coeff;
H[0] -= coeff;
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::divide_by_Z_on_coset(std::vector<FieldT> &P)
{
const FieldT coset = FieldT::multiplicative_generator;
const FieldT Z_inverse_at_coset = this->compute_Z(coset).inverse();
for (size_t i = 0; i < this->m; ++i)
{
P[i] *= Z_inverse_at_coset;
}
}
} // libsnark
#endif // BASIC_RADIX2_DOMAIN_TCC_

View File

@@ -0,0 +1,48 @@
/** @file
*****************************************************************************
Declaration of interfaces for auxiliary functions for the "basic radix-2" evaluation domain.
These functions compute the radix-2 FFT (in single- or multi-thread mode) and,
also compute Lagrange coefficients.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_AUX_HPP_
#define BASIC_RADIX2_DOMAIN_AUX_HPP_
namespace libsnark {
/**
* Compute the radix-2 FFT of the vector a over the set S={omega^{0},...,omega^{m-1}}.
*/
template<typename FieldT>
void _basic_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega);
/**
* A multi-thread version of _basic_radix2_FFT.
*/
template<typename FieldT>
void _parallel_basic_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega);
/**
* Translate the vector a to a coset defined by g.
*/
template<typename FieldT>
void _multiply_by_coset(std::vector<FieldT> &a, const FieldT &g);
/**
* Compute the m Lagrange coefficients, relative to the set S={omega^{0},...,omega^{m-1}}, at the field element t.
*/
template<typename FieldT>
std::vector<FieldT> _basic_radix2_lagrange_coeffs(const size_t m, const FieldT &t);
} // libsnark
#include "algebra/evaluation_domain/domains/basic_radix2_domain_aux.tcc"
#endif // BASIC_RADIX2_DOMAIN_AUX_HPP_

View File

@@ -0,0 +1,242 @@
/** @file
*****************************************************************************
Implementation of interfaces for auxiliary functions for the "basic radix-2" evaluation domain.
See basic_radix2_domain_aux.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_AUX_TCC_
#define BASIC_RADIX2_DOMAIN_AUX_TCC_
#include <cassert>
#ifdef MULTICORE
#include <omp.h>
#endif
#include "algebra/fields/field_utils.hpp"
#include "common/profiling.hpp"
#include "common/utils.hpp"
namespace libsnark {
#ifdef MULTICORE
#define _basic_radix2_FFT _basic_parallel_radix2_FFT
#else
#define _basic_radix2_FFT _basic_serial_radix2_FFT
#endif
/*
Below we make use of pseudocode from [CLRS 2n Ed, pp. 864].
Also, note that it's the caller's responsibility to multiply by 1/N.
*/
template<typename FieldT>
void _basic_serial_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega)
{
const size_t n = a.size(), logn = log2(n);
assert(n == (1u << logn));
/* swapping in place (from Storer's book) */
for (size_t k = 0; k < n; ++k)
{
const size_t rk = bitreverse(k, logn);
if (k < rk)
std::swap(a[k], a[rk]);
}
size_t m = 1; // invariant: m = 2^{s-1}
for (size_t s = 1; s <= logn; ++s)
{
// w_m is 2^s-th root of unity now
const FieldT w_m = omega^(n/(2*m));
asm volatile ("/* pre-inner */");
for (size_t k = 0; k < n; k += 2*m)
{
FieldT w = FieldT::one();
for (size_t j = 0; j < m; ++j)
{
const FieldT t = w * a[k+j+m];
a[k+j+m] = a[k+j] - t;
a[k+j] += t;
w *= w_m;
}
}
asm volatile ("/* post-inner */");
m *= 2;
}
}
template<typename FieldT>
void _basic_parallel_radix2_FFT_inner(std::vector<FieldT> &a, const FieldT &omega, const size_t log_cpus)
{
const size_t num_cpus = 1ul<<log_cpus;
const size_t m = a.size();
const size_t log_m = log2(m);
assert(m == 1ul<<log_m);
if (log_m < log_cpus)
{
_basic_serial_radix2_FFT(a, omega);
return;
}
enter_block("Shuffle inputs");
std::vector<std::vector<FieldT> > tmp(num_cpus);
for (size_t j = 0; j < num_cpus; ++j)
{
tmp[j].resize(1ul<<(log_m-log_cpus), FieldT::zero());
}
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t j = 0; j < num_cpus; ++j)
{
const FieldT omega_j = omega^j;
const FieldT omega_step = omega^(j<<(log_m - log_cpus));
FieldT elt = FieldT::one();
for (size_t i = 0; i < 1ul<<(log_m - log_cpus); ++i)
{
for (size_t s = 0; s < num_cpus; ++s)
{
// invariant: elt is omega^(j*idx)
const size_t idx = (i + (s<<(log_m - log_cpus))) % (1u << log_m);
tmp[j][i] += a[idx] * elt;
elt *= omega_step;
}
elt *= omega_j;
}
}
leave_block("Shuffle inputs");
enter_block("Execute sub-FFTs");
const FieldT omega_num_cpus = omega^num_cpus;
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t j = 0; j < num_cpus; ++j)
{
_basic_serial_radix2_FFT(tmp[j], omega_num_cpus);
}
leave_block("Execute sub-FFTs");
enter_block("Re-shuffle outputs");
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < num_cpus; ++i)
{
for (size_t j = 0; j < 1ul<<(log_m - log_cpus); ++j)
{
// now: i = idx >> (log_m - log_cpus) and j = idx % (1u << (log_m - log_cpus)), for idx = ((i<<(log_m-log_cpus))+j) % (1u << log_m)
a[(j<<log_cpus) + i] = tmp[i][j];
}
}
leave_block("Re-shuffle outputs");
}
template<typename FieldT>
void _basic_parallel_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega)
{
#ifdef MULTICORE
const size_t num_cpus = omp_get_max_threads();
#else
const size_t num_cpus = 1;
#endif
const size_t log_cpus = ((num_cpus & (num_cpus - 1)) == 0 ? log2(num_cpus) : log2(num_cpus) - 1);
#ifdef DEBUG
print_indent(); printf("* Invoking parallel FFT on 2^%zu CPUs (omp_get_max_threads = %zu)\n", log_cpus, num_cpus);
#endif
if (log_cpus == 0)
{
_basic_serial_radix2_FFT(a, omega);
}
else
{
_basic_parallel_radix2_FFT_inner(a, omega, log_cpus);
}
}
template<typename FieldT>
void _multiply_by_coset(std::vector<FieldT> &a, const FieldT &g)
{
//enter_block("Multiply by coset");
FieldT u = g;
for (size_t i = 1; i < a.size(); ++i)
{
a[i] *= u;
u *= g;
}
//leave_block("Multiply by coset");
}
template<typename FieldT>
std::vector<FieldT> _basic_radix2_lagrange_coeffs(const size_t m, const FieldT &t)
{
if (m == 1)
{
return std::vector<FieldT>(1, FieldT::one());
}
assert(m == (1u << log2(m)));
const FieldT omega = get_root_of_unity<FieldT>(m);
std::vector<FieldT> u(m, FieldT::zero());
/*
If t equals one of the roots of unity in S={omega^{0},...,omega^{m-1}}
then output 1 at the right place, and 0 elsewhere
*/
if ((t^m) == (FieldT::one()))
{
FieldT omega_i = FieldT::one();
for (size_t i = 0; i < m; ++i)
{
if (omega_i == t) // i.e., t equals omega^i
{
u[i] = FieldT::one();
return u;
}
omega_i *= omega;
}
}
/*
Otherwise, if t does not equal any of the roots of unity in S,
then compute each L_{i,S}(t) as Z_{S}(t) * v_i / (t-\omega^i)
where:
- Z_{S}(t) = \prod_{j} (t-\omega^j) = (t^m-1), and
- v_{i} = 1 / \prod_{j \neq i} (\omega^i-\omega^j).
Below we use the fact that v_{0} = 1/m and v_{i+1} = \omega * v_{i}.
*/
const FieldT Z = (t^m)-FieldT::one();
FieldT l = Z * FieldT(m).inverse();
FieldT r = FieldT::one();
for (size_t i = 0; i < m; ++i)
{
u[i] = l * (t - r).inverse();
l *= omega;
r *= omega;
}
return u;
}
} // libsnark
#endif // BASIC_RADIX2_DOMAIN_AUX_TCC_

View File

@@ -0,0 +1,125 @@
/** @file
*****************************************************************************
Declaration of interfaces for evaluation domains.
Roughly, given a desired size m for the domain, the constructor selects
a choice of domain S with size ~m that has been selected so to optimize
- computations of Lagrange polynomials, and
- FFT/iFFT computations.
An evaluation domain also provides other other functions, e.g., accessing
individual elements in S or evaluating its vanishing polynomial.
The descriptions below make use of the definition of a *Lagrange polynomial*,
which we recall. Given a field F, a subset S=(a_i)_i of F, and an index idx
in {0,...,|S-1|}, the idx-th Lagrange polynomial (wrt to subset S) is defined to be
\f[ L_{idx,S}(z) := prod_{k \neq idx} (z - a_k) / prod_{k \neq idx} (a_{idx} - a_k) \f]
Note that, by construction:
\f[ \forall j \neq idx: L_{idx,S}(a_{idx}) = 1 \text{ and } L_{idx,S}(a_j) = 0 \f]
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef EVALUATION_DOMAIN_HPP_
#define EVALUATION_DOMAIN_HPP_
#include <memory>
namespace libsnark {
/**
* An evaluation domain.
*/
template<typename FieldT>
class evaluation_domain {
public:
const size_t m;
/**
* Construct an evaluation domain S of size m, if possible.
*
* (See the function get_evaluation_domain below.)
*/
evaluation_domain(const size_t m) : m(m) {};
/**
* Get the idx-th element in S.
*/
virtual FieldT get_element(const size_t idx) = 0;
/**
* Compute the FFT, over the domain S, of the vector a.
*/
virtual void FFT(std::vector<FieldT> &a) = 0;
/**
* Compute the inverse FFT, over the domain S, of the vector a.
*/
virtual void iFFT(std::vector<FieldT> &a) = 0;
/**
* Compute the FFT, over the domain g*S, of the vector a.
*/
virtual void cosetFFT(std::vector<FieldT> &a, const FieldT &g) = 0;
/**
* Compute the inverse FFT, over the domain g*S, of the vector a.
*/
virtual void icosetFFT(std::vector<FieldT> &a, const FieldT &g) = 0;
/**
* Evaluate all Lagrange polynomials.
*
* The inputs are:
* - an integer m
* - an element t
* The output is a vector (b_{0},...,b_{m-1})
* where b_{i} is the evaluation of L_{i,S}(z) at z = t.
*/
virtual std::vector<FieldT> lagrange_coeffs(const FieldT &t) = 0;
/**
* Evaluate the vanishing polynomial of S at the field element t.
*/
virtual FieldT compute_Z(const FieldT &t) = 0;
/**
* Add the coefficients of the vanishing polynomial of S to the coefficients of the polynomial H.
*/
virtual void add_poly_Z(const FieldT &coeff, std::vector<FieldT> &H) = 0;
/**
* Multiply by the evaluation, on a coset of S, of the inverse of the vanishing polynomial of S.
*/
virtual void divide_by_Z_on_coset(std::vector<FieldT> &P) = 0;
};
/**
* Return an evaluation domain object in which the domain S has size |S| >= min_size.
* The function chooses from different supported domains, depending on min_size.
*/
template<typename FieldT>
std::shared_ptr<evaluation_domain<FieldT> > get_evaluation_domain(const size_t min_size);
/**
* Naive evaluation of a *single* Lagrange polynomial, used for testing purposes.
*
* The inputs are:
* - an integer m
* - a domain S = (a_{0},...,a_{m-1}) of size m
* - a field element element t
* - an index idx in {0,...,m-1}
* The output is the polynomial L_{idx,S}(z) evaluated at z = t.
*/
template<typename FieldT>
FieldT lagrange_eval(const size_t m, const std::vector<FieldT> &domain, const FieldT &t, const size_t idx);
} // libsnark
#include "algebra/evaluation_domain/evaluation_domain.tcc"
#endif // EVALUATION_DOMAIN_HPP_

View File

@@ -0,0 +1,117 @@
/** @file
*****************************************************************************
Imeplementation of interfaces for evaluation domains.
See evaluation_domain.hpp .
We currently implement, and select among, three types of domains:
- "basic radix-2": the domain has size m = 2^k and consists of the m-th roots of unity
- "extended radix-2": the domain has size m = 2^{k+1} and consists of "the m-th roots of unity" union "a coset"
- "step radix-2": the domain has size m = 2^k + 2^r and consists of "the 2^k-th roots of unity" union "a coset of 2^r-th roots of unity"
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef EVALUATION_DOMAIN_TCC_
#define EVALUATION_DOMAIN_TCC_
#include <cassert>
#include "algebra/fields/field_utils.hpp"
#include "algebra/evaluation_domain/domains/basic_radix2_domain.hpp"
namespace libsnark {
template<typename FieldT>
std::shared_ptr<evaluation_domain<FieldT> > get_evaluation_domain(const size_t min_size)
{
assert(min_size > 1);
const size_t log_min_size = log2(min_size);
assert(log_min_size <= (FieldT::s+1));
std::shared_ptr<evaluation_domain<FieldT> > result;
if (min_size == (1u << log_min_size))
{
if (log_min_size == FieldT::s+1)
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: extended_radix2\n");
}
assert(0);
}
else
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: basic_radix2\n");
}
result.reset(new basic_radix2_domain<FieldT>(min_size));
}
}
else
{
const size_t big = 1ul<<(log2(min_size)-1);
const size_t small = min_size - big;
const size_t rounded_small = (1ul<<log2(small));
if (big == rounded_small)
{
if (log2(big + rounded_small) < FieldT::s+1)
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: basic_radix2\n");
}
result.reset(new basic_radix2_domain<FieldT>(big + rounded_small));
}
else
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: extended_radix2\n");
}
assert(0);
}
}
else
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: step_radix2\n");
}
assert(0);
}
}
return result;
}
template<typename FieldT>
FieldT lagrange_eval(const size_t m, const std::vector<FieldT> &domain, const FieldT &t, const size_t idx)
{
assert(m == domain.size());
assert(idx < m);
FieldT num = FieldT::one();
FieldT denom = FieldT::one();
for (size_t k = 0; k < m; ++k)
{
if (k == idx)
{
continue;
}
num *= t - domain[k];
denom *= domain[idx] - domain[k];
}
return num * denom.inverse();
}
} // libsnark
#endif // EVALUATION_DOMAIN_TCC_