diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..20fab5e --- /dev/null +++ b/Makefile @@ -0,0 +1,81 @@ +WHICH := $(shell which which) +PWD := $(shell $(WHICH) pwd) + +# +WD := $(shell $(PWD)) +BUILD_DIR := bin +EXE_DIR := $(WD) +BLDDIR := $(WD)/$(BUILD_DIR) +GADGETLIB3_SRC_DIR := tinyram/gadgetlib/gadgetlib +FFTLIB_SRC_DIR := algebra/FFT/src + +ALGEBRALIB_DIR := $(WD)/algebra/algebralib +LIBSTARK_DIR := $(WD)/libstark +GADGETLIB3_DIR := $(WD)/$(GADGETLIB3_SRC_DIR) +TINYRAM_DIR := $(WD)/tinyram/stark-tinyram +FFTLIB_DIR := $(WD)/algebra/FFT + +.PHONY: \ + libstark libstark-clean \ + stark-tinyram stark-tinyram-clean \ + fft fft-clean \ + algebralib algebralib-clean \ + gadgetlib gadgetlib-clean \ + clean + +default: stark-tinyram + +libstark: + $(MAKE) -C $(LIBSTARK_DIR) \ + BLDDIR=$(BLDDIR)/libstark \ + FFTINC=$(FFTLIB_DIR)/src \ + ALGEBRAINC=$(ALGEBRALIB_DIR)/headers + +libstark-clean: + $(MAKE) clean -C $(LIBSTARK_DIR) BLDDIR=$(BLDDIR)/libstark + +stark-tinyram: gadgetlib fft algebralib libstark + $(MAKE) -C $(TINYRAM_DIR) \ + BLDDIR=$(BLDDIR)/stark-tinyram \ + EXEDIR=$(EXE_DIR) \ + FFTINC=$(FFTLIB_DIR)/src \ + FFTLIBLNKDIR=$(BLDDIR)/fft \ + ALGEBRAINC=$(ALGEBRALIB_DIR)/headers \ + ALGEBRALNKDIR=$(BLDDIR)/algebralib \ + LIBSTARKINC=$(LIBSTARK_DIR)/src \ + LIBSTARKLINKDIR=$(BLDDIR)/libstark \ + GADGET3INC=$(GADGETLIB3_DIR)/../. \ + GADGET3LNKDIR=$(BLDDIR)/gadgetlib + +stark-tinyram-clean: + $(MAKE) clean -C $(TINYRAM_DIR) \ + BLDDIR=$(BLDDIR)/stark-tinyram \ + EXEDIR=$(EXE_DIR) + +fft: + $(MAKE) -C $(FFTLIB_DIR) BLDDIR=$(BLDDIR)/fft + +fft-clean: + $(MAKE) clean -C $(FFTLIB_DIR) BLDDIR=$(BLDDIR)/fft + +algebralib: + $(MAKE) -C $(ALGEBRALIB_DIR) \ + BLDDIR=$(BLDDIR)/algebralib FFTINC=$(FFTLIB_DIR)/src + +algebralib-clean: + $(MAKE) clean -C $(ALGEBRALIB_DIR) BLDDIR=$(BLDDIR)/algebralib + +gadgetlib: + $(MAKE) -C $(GADGETLIB3_DIR) \ + GADGETINC=$(GADGETLIB3_DIR)/. \ + ALGEBRAINC=$(ALGEBRALIB_DIR)/headers \ + ALGEBRALIBLINKDIR=$(BLDDIR)/algebralib \ + FFTLIBLNKDIR=$(BLDDIR)/fft \ + FFTINC=$(FFTLIB_DIR) FFTLIBLNKDIR=$(BLDDIR)/fft \ + BLDDIR=$(BLDDIR)/gadgetlib + +gadgetlib-clean: + $(MAKE) -C $(GADGETLIB3_DIR) BLDDIR=$(BLDDIR)/gadgetlib clean + +clean: gadgetlib-clean stark-tinyram-clean libstark-clean fft-clean algebralib-clean + rm -r $(BLDDIR) diff --git a/algebra/FFT/Makefile b/algebra/FFT/Makefile new file mode 100644 index 0000000..996bc23 --- /dev/null +++ b/algebra/FFT/Makefile @@ -0,0 +1,29 @@ +CC=g++ +CPPFLAGS=-std=c++14 +CFLAGS=-O3 -g -Wall -fmessage-length=0 -fopenmp -mavx -maes -mtune=native + +WHICH := $(shell which which) +MKDIR := $(shell $(WHICH) mkdir) +DIRNAME := $(shell $(WHICH) dirname) + +CFLAGS+=-mpclmul +CPPFLAGS+=-mpclmul +INCFLAGS=-Isrc +TARGET=$(BLDDIR)/libFFT.a + +SRCS:= $(shell ls src/*.cpp) +OBJS=$(addprefix $(BLDDIR)/, $(SRCS:.cpp=.o)) + +$(BLDDIR)/%.o: %.cpp +# @echo 'Building file: $@ ($<)' + @$(MKDIR) -p $(shell $(DIRNAME) $@) + $(CC) $(CFLAGS) $(CPPFLAGS) $(INCFLAGS) -c -o "$@" "$<" + +all: $(TARGET) + +clean: + $(RM) -f $(TARGET) $(OBJS) $(DEPS) + +$(TARGET): $(OBJS) +# @echo 'Building target: $@' + ar -r "$@" $(OBJS) $(LIBS) diff --git a/algebra/FFT/src/Basis.cpp b/algebra/FFT/src/Basis.cpp new file mode 100644 index 0000000..e718702 --- /dev/null +++ b/algebra/FFT/src/Basis.cpp @@ -0,0 +1,81 @@ +/* + * Basis.cpp + * + * Created on: Jul 2, 2014 + * Author: matan + */ +#include +#include +#include "Basis.h" +#include +namespace FFF { + +void Basis::getElement(Basis& b, Element& e,idx_t idx){ + b.getShift(e); + for(unsigned int i = 0 ; i < b.size ; ++i){ + if(idx & 1) + Element::c_add(e,b.b[i],e); + idx>>=1; + } +} +Basis::Basis() : + b((Element*)malloc(sizeof(Element))), shift() , size(1){}; +Basis::Basis(Basis& that) : +shift(that.shift), size(that.size) +{ + b = ((Element*)malloc(sizeof(Element)*that.size)); + memcpy(this->b, that.b, sizeof(Element)*that.size); +} +Basis::Basis(Element* e, len_t l, Element& s): + b((Element*)malloc(sizeof(Element)*l)), shift(s),size(l) +{ + memcpy(b,e,sizeof(Element)*l); +} +//Ariel:buggy method, but not actually used - so commented out +//void Basis::setBasis(Element* e, len_t l, Element& s){ +// this->~Basis(); +// this->b=(Element*)malloc(sizeof(Element)*l); +// this->shift = s; +// this-> size = l; +//} +void Basis::operator=(const Basis& b){ + this->~Basis(); + this->b= (Element*)malloc(sizeof(Element)*b.size); + memcpy(this->b,b.b,sizeof(Element)*b.size); + size = b.size; + shift=b.shift; + } +unsigned int Basis::getSize() const { + return this->size; +} +const Element& Basis::getShift() const{ + return this->shift; +} +void Basis::getBasis(Element* D) const +{ + memcpy(D,this->b,sizeof(Element)*this->size); +} +void Basis::getShift(Element& e) const +{ + Element::assign(e,this->shift); +} + +Element* Basis::getBasis() const{ + return this->b; +} +void Basis::printBasis() const{ + std::cout << "Elements:" << std::endl; + for(unsigned int i = 0 ; i < this->size ; ++i){ + Element::printElement(this->b[i]); + std::cout << std::endl; + } + std:: cout << "Shift:" << std::endl; + Element::printElement(this->shift); + std::cout << std::endl << std::endl; + +} +Basis::~Basis() { + free(b); +} + +} /* namespace FFF */ diff --git a/algebra/FFT/src/Basis.h b/algebra/FFT/src/Basis.h new file mode 100644 index 0000000..1eeb56f --- /dev/null +++ b/algebra/FFT/src/Basis.h @@ -0,0 +1,40 @@ +/* + * Basis.h + * + * Created on: Jul 2, 2014 + * Author: matan + */ + +#ifndef BASIS_H_ +#define BASIS_H_ +#include "Definitions.h" +#include "Element.h" +namespace FFF { + +class Basis { + Element* b; + Element shift; + len_t size; +public: + Basis(); + Basis(Basis& b); + void operator=(const Basis& b); + Basis(Element* e, len_t l,Element& s); + void setBasis(Element* e, len_t l, Element& s); + static void getElement(Basis& b, Element& e,idx_t idx); + unsigned int getSize() const ; + + /* + * Copies into D the elements of the basis. + * D has to be preallocated. + */ + void getBasis(Element* D)const; + Element* getBasis() const; + void getShift(Element& e) const; + const Element& getShift() const; + void printBasis() const; + ~Basis(); +}; + +} /* namespace FFF */ +#endif /* BASIS_H_ */ diff --git a/algebra/FFT/src/Chunk.cu b/algebra/FFT/src/Chunk.cu new file mode 100644 index 0000000..084ee99 --- /dev/null +++ b/algebra/FFT/src/Chunk.cu @@ -0,0 +1,426 @@ +//#include "Chunk.cuh" +//#include +//#include +//#include +// +//using namespace std; +//namespace FFF{ +//__constant__ idx_t p_Mod[max_nonzero_coefs_in_mod]; +//__constant__ idx_t p_ModLen; +//__constant__ Element element_mul; +//__constant__ Chunk c; +// +// +//__device__ void a_chunkToNormal(Chunk *d_a, Elements_Chunk *d_b, idx_t idx) +//{ +// cell_t ans = 0; +// idx_t element_idx = idx & andMask(Chunk::log_elements_in_chunk); +// idx_t cell_idx = idx >> Chunk::log_elements_in_chunk; +// for(unsigned int i = cell_idx<v[i])>>(element_idx))&1))<<(i-(cell_idx<e[element_idx].c[cell_idx]=ans; +//} +//__global__ void k_chunkToNormal(Chunk *d_a,Elements_Chunk *d_b , len_t len) +//{ +// const unsigned int threads_in_chunk = Chunk::elements_in_chunk * Element::element_len; +// __shared__ Chunk input[max_block_size / threads_in_chunk]; +// idx_t idx = threadIdx.x + blockDim.x*blockIdx.x; +// if(idx >= len*threads_in_chunk) +// return; +// idx_t chunkIdx = (idx) / (Element::element_len*Chunk::elements_in_chunk); +// idx_t in_chunkIdx = (idx & (Element::element_len * Chunk::elements_in_chunk - 1)); +// idx_t chunks_in_block = blockDim.x / Chunk::cells_in_chunk; +// idx_t inBlockChunkIdx = chunkIdx & (threads_in_chunk-1); +// for(unsigned int i = 0 ; i < sizeof(cell_t)/sizeof(chunk_cell_t) ; ++i){ +// input[inBlockChunkIdx].v[in_chunkIdx + i*threads_in_chunk] = d_a[chunkIdx].v[in_chunkIdx+i*threads_in_chunk]; +// } +// a_chunkToNormal(&(input[inBlockChunkIdx]), &(d_b[chunkIdx]),in_chunkIdx); +//} +//__host__ void Chunk::chunkToNormal(Chunk(*h_a), Elements_Chunk(*h_b), len_t len, bool copy) +//{ +// //Declare device variables +// Chunk (*d_a); +// Elements_Chunk (*d_b); +// +// const unsigned int num_element = len*elements_in_chunk; +// const unsigned int threads = Element::element_len * num_element; +// +// //Define Block and Grid Size. +// dim3 blockSize(max_block_size,1,1); +// dim3 gridSize(sizeCiel(threads,max_block_size),1,1); +// if(copy){ +// //Allocate Memory on GPU. (global) +// cudaMalloc(&d_a,sizeof(Chunk)*len); +// cudaMalloc(&d_b,sizeof(Elements_Chunk)*len); +// +// //Copy memory to GPU. +// cudaMemcpy(d_a,h_a,sizeof(Chunk)*len,cudaMemcpyHostToDevice); +// } else { +// d_a = h_a; +// d_b = h_b; +// } +// +// //Launch Kernel +// k_chunkToNormal<<>>(d_a,d_b,len); +// if(copy){ +// //Copy results back to memory +// cudaMemcpy(h_b,d_b,sizeof(Elements_Chunk)*len,cudaMemcpyDeviceToHost); +// +// //Free allocated memory. +// cudaFree(d_a); +// cudaFree(d_b); +// } +//} +//__device__ void a_normalToChunk(Elements_Chunk *d_a, Chunk *d_b, idx_t idx) +//{ +// chunk_cell_t ans = 0; +// idx_t cell_idx = idx>>Element::log_bits_in_cell; +// for(unsigned int i = 0 ; i < Chunk::elements_in_chunk ; ++i) +// ans^=((((d_a->e[i].c[cell_idx])>>(idx& andMask(Element::log_bits_in_cell)))&1)<v[idx]=ans; +//} +//__global__ void k_normalToChunk(Elements_Chunk *d_a,Chunk *d_b , len_t len) +//{ +// idx_t idx = threadIdx.x + blockDim.x*blockIdx.x; +// if(idx >= (len<> Chunk::log_cells_in_chunk; +// idx_t in_chunkIdx = (idx & andMask(Chunk::log_cells_in_chunk)); +// a_normalToChunk(&(d_a[chunkIdx]),&(d_b[chunkIdx]),in_chunkIdx); +//} +//__host__ void Chunk::normalToChunk(Elements_Chunk(*h_a), Chunk (*h_b), len_t len,bool copy) +//{ +// +// //Declare device variables +// Elements_Chunk (*d_a); +// Chunk (*d_b); +// +// const unsigned int threads = len<>>(d_a,d_b,len); +// +// //Copy results back to memory +// if(copy){ +// cudaMemcpy(h_b,d_b,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); +// //Free allocated memory. +// cudaFree(d_a); +// cudaFree(d_b); +// } +// +//} +//__host__ void Chunk::setMod(){ +// cudaMemcpyToSymbol(p_Mod,&(Element::irr_poly_index[ord>>log_warp_size]),sizeof(idx_t)*max_nonzero_coefs_in_mod); +// cudaMemcpyToSymbol(p_ModLen,&(Element::mod_len[ord>>log_warp_size]),sizeof(idx_t)); +//} +//__device__ void Chunk::chunk_reduce_xor(Chunk *a, Chunk *c_bottom, Chunk*c_top, idx_t idx) +//{ +// chunk_cell_t ans=c_bottom->v[idx]; +// unsigned int temp_idx; +// for(idx_t i = 0 ; i < p_ModLen ; ++i) +// { +// for(idx_t j = 0 ; j < p_ModLen ; ++j) +// { +// temp_idx = idx+(ord<<1)-p_Mod[i]-p_Mod[j]; +// if(temp_idx >= (ord<<1)-p_Mod[j] && temp_idx < (ord<<1)) +// ans^=c_top->v[temp_idx-ord]; +// } +// } +// a->v[idx]^=ans; +//} +//__device__ void Chunk::chunk_xor(Chunk *a, Chunk* b, idx_t idx){ +// a->v[idx]^=b->v[idx]; +//} +//__device__ void Chunk::chunk_reduce_xor(Chunk *a, Chunk *c_bottom, idx_t idx,Chunk* to_xor ,int shift) +//{ +// unsigned int k = p_ModLen; +// for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) +// for(unsigned int j = 0 ; j+1 < k ; ++j) +// { +// c_bottom->v[(ord>>1)+idx+i+p_Mod[j]]^=c_bottom->v[(ord>>1)+ord+idx+i]; +// } +// for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) +// for(unsigned int j = 0 ; (j+1) < k ; ++j) +// { +// c_bottom->v[idx+i+p_Mod[j]]^=c_bottom->v[ord+idx+i]; +// } +// for(unsigned int i = 0 ; i < ord ; i+=warp_size){ +// to_xor->v[idx+i]^=(c_bottom->v[idx+i]>>shift); +// } +//} +// +//__device__ void Chunk::chunk_reduce(Chunk *a, Chunk *c_bottom, idx_t idx) +//{ +// unsigned int k = p_ModLen; +// for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) +// for(unsigned int j = 0 ; j+1 < k ; ++j) +// { +// c_bottom->v[(ord>>1)+idx+i+p_Mod[j]]^=c_bottom->v[(ord>>1)+ord+idx+i]; +// } +// for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) +// for(unsigned int j = 0 ; (j+1) < k ; ++j) +// { +// c_bottom->v[idx+i+p_Mod[j]]^=c_bottom->v[ord+idx+i]; +// } +// for(unsigned int i = 0 ; i < ord ; i+=warp_size){ +// a->v[idx+i]=c_bottom->v[idx+i]; +// } +//} +//__device__ void Chunk::chunkClmul(Chunk (*a), Element (*e), idx_t idx, Chunk (*c)) +//{ +// chunk_cell_t my_ans[2][(ord>>(log_warp_size))]={0}; +// for(unsigned int k = 0 ; k < ord ; ++k) +// { +// if(EXTRACT_BIT(e->c,k)) +// for(unsigned int t = 0 ; t < (ord>>log_warp_size); ++t) +// { +// int b = (k>(idx+warp_size*t)); +// my_ans[b][t]^=a->v[idx+warp_size*t+(b<>log_warp_size); ++i) +// { +// c->v[idx+i*warp_size] = my_ans[0][i]; +// c->v[ord+idx+i*warp_size] = my_ans[1][i]; +// } +//} +//__device__ void Chunk::aux_k_clmul(Chunk *a, Element* e, len_t len,Chunk* c_shared) +//{ +// +// idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; +// const idx_t chunk_idx = (idx >> Chunk::log_threads_in_chunk); +// const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); +// const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); +// Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); +// for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size) +// my_shared_chunk->v[in_chunk_idx+i]=a[chunk_idx].v[in_chunk_idx+i]; +// Chunk::chunkClmul(my_shared_chunk,e,in_chunk_idx,my_shared_chunk); +// Chunk::chunk_reduce(a+chunk_idx,my_shared_chunk,in_chunk_idx); +//} +//__global__ void k_clmul(Chunk *a,Element *e,len_t len ) +//{ +// const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; +// __shared__ Chunk c_shared[shared_len<<1]; +// idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; +// if(idx>=(len<>>(d_a,d_e,len); +//#ifdef __MEASURE +// cudaEventRecord(stop,0); +//#endif +// +// //Copy results to host +// cudaMemcpy(h_res,d_a,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); +// //Free allocated memory. +// cudaFree(d_a); +// cudaFree(d_e); +//#ifdef __MEASURE +// cudaEventElapsedTime(&time,start,stop); +// printf("Time for the mul: %f ms on %d chunks \n",time,len); +//#endif +//} +// +//__global__ void k_add(Chunk (*a), Chunk (*b), len_t l) +//{ +// unsigned int idx = threadIdx.x+blockIdx.x*blockDim.x; +// if(idx>=l*Chunk::cells_in_chunk) +// return; +// ((chunk_cell_t*)a)[idx]^=((chunk_cell_t*)b)[idx]; +//} +//__host__ void Chunk::add(Chunk (*h_a),Chunk (*h_b),len_t len) +//{ +// +// //Declare device variables +// Chunk (*d_a); +// Chunk (*d_b); +// +// //Define Block and Grid Size. +// dim3 blockSize(max_block_size,1,1); +// dim3 gridSize(sizeCiel(max_block_size,len),1,1); +// +// //Allocate Memory on GPU. (global) +// cudaMalloc(&d_a,sizeof(Chunk)*len); +// cudaMalloc(&d_b,sizeof(Chunk)*len); +// +// //Copy memory to GPU. +// cudaMemcpy(d_a,h_a,sizeof(Chunk)*len,cudaMemcpyHostToDevice); +// cudaMemcpy(d_b,h_b,sizeof(Chunk)*len,cudaMemcpyHostToDevice); +// +// //Launch Kernel +// k_add<<>>(d_a,d_b,len); +// +// //Copy results to CPU memory +// cudaMemcpy(h_a,d_a,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); +// +// //Free allocated memory. +// cudaFree(d_a); +// cudaFree(d_b); +//} +//__host__ void Chunk::print() const { +// for(unsigned int i = 0 ; i < cells_in_chunk ; ++i){ +// cout << bitset(this->v[i])<e[i]); +// cout<>(log_warp_size))]={0}; +// for(unsigned int k = 0 ; k < ord ; ++k) +// for(unsigned int t = 0 ; t < (ord>>log_warp_size); ++t) +// { +// int b = (k>(idx+warp_size*t)); +// my_ans[b][t]^=a.v[idx+warp_size*t+(b<>log_warp_size); ++i) +// { +// c->v[idx+i*warp_size] = my_ans[0][i]; +// c->v[ord+idx+i*warp_size] = my_ans[1][i]; +// } +//} +////Mul chunk by another chunk +//__global__ void k_mul_chunk(Chunk* cs, Chunk* c, len_t cs_len) +//{ +// const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; +// __shared__ Chunk c_shared[shared_len<<1]; +// idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; +// if(idx>=(cs_len<> Chunk::log_threads_in_chunk); +// const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); +// const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); +// Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); +// for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ +// my_shared_chunk->v[in_chunk_idx+i]=cs[chunk_idx].v[in_chunk_idx+i]; +// my_shared_chunk[1].v[in_chunk_idx+i]=c->v[in_chunk_idx+i]; +// } +// Chunk::clmul_by_chunk(my_shared_chunk[0],my_shared_chunk[1],in_chunk_idx,my_shared_chunk); +// Chunk::chunk_reduce(cs+chunk_idx,my_shared_chunk,in_chunk_idx); +//} +//__global__ void k_mul_chunk_xor(Chunk* cs, Chunk* c, len_t cs_len,Chunk* to_xor, int shift = 0) +//{ +// const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; +// __shared__ Chunk c_shared[shared_len<<1]; +// idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; +// if(idx>=(cs_len<> Chunk::log_threads_in_chunk); +// const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); +// const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); +// Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); +// for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ +// my_shared_chunk->v[in_chunk_idx+i]=cs[chunk_idx].v[in_chunk_idx+i]; +// my_shared_chunk[1].v[in_chunk_idx+i]=c->v[in_chunk_idx+i]; +// } +// Chunk::clmul_by_chunk(my_shared_chunk[0],my_shared_chunk[1],in_chunk_idx,my_shared_chunk); +// Chunk::chunk_reduce_xor(cs+chunk_idx,my_shared_chunk,in_chunk_idx,to_xor,shift); +//} +////Mul a chunk by a chunk +//void Chunk::chunk_mul(Chunk (* h_a), Chunk (*h_b) , len_t len, Chunk (*h_res), bool copy, bool do_xor, int shift){ +//#ifdef __MEASURE +// cudaEvent_t start,stop; +// float time; +// cudaEventCreate(&start); +// cudaEventCreate(&stop); +//#endif +// //Declare device variables +// Chunk (*d_a); +// Chunk (*d_b); +// +// //Define Block and Grid Size. +// dim3 blockSize(max_block_size,1,1); +// dim3 gridSize(sizeCiel(len<>>(d_a,d_b,len,d_a,shift); +// else +// k_mul_chunk<<>>(d_a,d_b,len); +//#ifdef __MEASURE +// cudaEventRecord(stop,0); +//#endif +// +// if(copy){ +// //Copy results to host +// cudaMemcpy(h_res,d_a,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); +// //Free allocated memory. +// cudaFree(d_a); +// cudaFree(d_b); +// } +//#ifdef __MEASURE +// cudaEventElapsedTime(&time,start,stop); +// printf("Time for the mul: %f ms on %d chunks \n",time,len); +//#endif +//} +//} diff --git a/algebra/FFT/src/Chunk.cuh b/algebra/FFT/src/Chunk.cuh new file mode 100644 index 0000000..4b88d8c --- /dev/null +++ b/algebra/FFT/src/Chunk.cuh @@ -0,0 +1,69 @@ +/* + * Chunk.cuh + * + * Created on: Jul 29, 2014 + * Author: matan + */ + +#ifndef CHUNK_CUH_ +#define CHUNK_CUH_ +#ifdef __CUDACC__ +#define __CUDA_RUNNABLE__ __device__ __host__ +#define __CUDA_DEVICE__ __device__ +#else +#define __CUDA_RUNNABLE__ +#define __CUDA_DEVICE__ +#endif +#include "Element.h" +#include +#include "Definitions.h" +namespace FFF { +class Elements_Chunk; + +class Chunk { +public: + Chunk(){}; + ~Chunk(){}; + chunk_cell_t v[Element::ord]; + static const ord_t log_ord = Element::log_ord; + static const ord_t ord = Element::ord; + static const unsigned int log_threads_in_chunk = log_warp_size; + static const unsigned int log_cells_in_chunk = Element::log_ord; + static const unsigned int cells_in_chunk = 1< + +#define __PARALLEL__ +//#define __GPU +//#define __MEASURE + +namespace FFF { + + /**** typedef *****/ +#ifdef __GNUC__ + typedef long long unsigned int idx_t; +#else // #ifdef GNUGC + typedef long long idx_t; +#endif // #ifdef GNUGC + typedef unsigned int chunk_cell_t; + typedef unsigned int ord_t; + typedef unsigned int len_t; + + /**** Macros ****/ + #define andMask(k) ((((cell_t)1)<<(k))-1) + #define TOGGLE_BIT_CELL(e,i) (e^=(((cell_t)1)<<(i))) + #define TOGGLE_BIT(e,i) (TOGGLE_BIT_CELL((e)[(i)>>Element::log_bits_in_cell],(i) & andMask(Element::log_bits_in_cell))) + #define EXTRACT_BIT_CELL(e,i) ((e)&(((cell_t)1)<<(i))) + #define EXTRACT_BIT(e,i) (EXTRACT_BIT_CELL(e[(i)>>log_bits_in_cell],i& andMask(log_bits_in_cell))) + #define Malloc(type,len) (type*)malloc(sizeof(type)*(len)); + #define MIN(a,b) ((a)>(b) ? (b) : (a)) + #define MAX(a,b) ((a)<(b) ? (b) : (a)) + + /**** Global Constants ****/ + const unsigned int max_nonzero_coefs_in_mod = 5; + const unsigned int bits_in_byte = 8; + const unsigned int log_warp_size = 5; + const unsigned int warp_size = 1< + void swap(T* a,T* b,len_t l){ + for(idx_t i = 0 ; i < l; ++i){ + a^=b; + b^=a; + a^=b; + } + } + + void set_omp_state(int omt); + +} + + +#endif /* DEFINITIONS_H_ */ diff --git a/algebra/FFT/src/Element.cpp b/algebra/FFT/src/Element.cpp new file mode 100644 index 0000000..93627f6 --- /dev/null +++ b/algebra/FFT/src/Element.cpp @@ -0,0 +1,566 @@ +/* + * Element.cpp + * + * Created on: May 31, 2014 + * Author: matan + */ +/* + * SSE SUPPORT + */ + +#ifdef WIN32 +//#include +#include +#endif // #ifdef WIN32 +#ifdef __GNUC__ +#include +#endif // #ifdef __GNUC__ +#include +#include +#include +#include +#include +#include +#include "Element.h" +#include "Definitions.h" +#include +#include +FFF::Element c_mulXorAux; +namespace FFF { +#ifdef WIN32 //TODO +#define __attribute__(x) +#define GetChar(value,index) (((char*)&value)[index]) +#define AS_8CHARS(a) \ + GetChar((a), 0), GetChar((a), 1), \ + GetChar((a), 2), GetChar((a), 3), \ + GetChar((a), 4), GetChar((a), 5), \ + GetChar((a), 6), GetChar((a), 7) +#define _mm_set1_epi64(x) {AS_8CHARS(x), AS_8CHARS(x)} +#endif +#ifdef WIN32 + __m128i mask = {-1,-1,-1,-1,-1,-1,-1,-1}; +#endif // #ifdef WIN32 +#ifdef __GNUC__ + __m128i mask = { (long long int)0b1111111111111111111111111111111111111111111111111111111111111111, 0 }; +#endif // #ifdef __GNUC__ +const idx_t Element::irr_poly_index[][max_nonzero_coefs_in_mod]={ + {0,(long long unsigned int)-1,(long long unsigned int)-1}, + {0,2,3,7,32}, + {0,1,3,4,64} + }; + +const cell_t Element::irr_poly_e[][Element::element_len]={ + {0}, + {0}, + {27} +}; +const len_t Element::mod_len[]= {3, 5, 5}; + +//--------------------------------------------------------------// + Element::Element(const Element& e){ + memcpy(this->c,e.c,sizeof(cell_t)*element_len); + } + void Element::operator=(const Element& e){ + memcpy(this->c,e.c,sizeof(cell_t)*element_len); + } + +void Element::deg(cell_t * a, int & i, len_t len){ + for(i=len-1;a[i]==0;--i){}; + if(i<0){ + i=0; + return; + } + cell_t t = a[i]; + idx_t offset=0; + i*=bits_in_cell; + for(idx_t j = bits_in_cell/2 ; j >0 ; j>>=1) + if((t>>(offset+j))!=0){ + offset+=j; + } + i+=offset; +} +void setZero(cell_t * a,len_t len){ + for(unsigned int i = 0 ; i < len ; ++i) + a[i]=0; +} +void setUnit(cell_t * a, len_t len){ + setZero(a,len); + a[0]=1; +} +bool isUnit(cell_t * a, len_t len){ + if(a[0]!=1) + return false; + for(idx_t i = 1; i < len ; ++i) + if(a[i]!=0) + return false; + return true; +} + +void swap(cell_t * a , cell_t * b,len_t len){ + for(unsigned int i = 0 ; i < len ; ++i) + { + a[i]^=b[i]; + b[i]^=a[i]; + a[i]^=b[i]; + } +} +void Element::assign_cells(cell_t* a, cell_t* b,len_t len){ + memcpy(a,b,sizeof(cell_t)*len); +} + + + +void Element::generateOrdElement(Element* e){ + memset(e->c,0,sizeof(cell_t)*element_len); + for(unsigned int i = 0 ; i < mod_len[ord>>5]-1;++i) + TOGGLE_BIT(e->c,irr_poly_index[ord>>5][i]); +} + +inline void Element::clmulXor(const cell_t* a,const cell_t* b, cell_t* res) +{ + _mm_storeu_si128((__m128i*)res, + _mm_xor_si128( + *((__m128i*)a), + _mm_clmulepi64_si128( + _mm_loadu_si128((__m128i*)a), + _mm_loadu_si128((__m128i*)b), + 0 + ) + ) + ); +// register __m128i v1,v2; +// ui64_m128i(v1,(const unsigned long*)a); +// ui64_m128i(v2,(const unsigned long*)b); +// m128i_ui64((unsigned long*)res,_mm_clmulepi64_si128(v1,v2,0)); +} +inline void Element::clmul(const cell_t* a,const cell_t* b, cell_t* res) +{ + _mm_clmulepi64_si128( + _mm_loadu_si128((__m128i*)a), + _mm_loadu_si128((__m128i*)b), + 0 + ); +} +/* + * CPU Operations + */ + void Element::c_add(const Element* const a,const Element* const b, Element* const c) + { + for(unsigned int i = 0 ; i < element_len ; ++i){ + c->c[i]=(a->c[i]^b->c[i]); + } + } + void Element::c_add(const Element& a,const Element& b, Element& c) + { + for(unsigned int i = 0 ; i < element_len ; ++i){ + c.c[i]=(a.c[i]^b.c[i]); + } + } + bool Element::equals(Element& a, Element& b){ + return !memcmp(a.c,b.c,sizeof(cell_t)*element_len); + } + void Element::c_mulXor(const Element& a,const Element& b,Element& c){ + cell_t clmul_res[element_len<<1]; + c_mul(&a,&b,(Element*)clmul_res); + vecXor(clmul_res,c.c,c.c,element_len); + } + +//specific for the irreducible 1 + x + x^3 + x^4 + x^64 +const cell_t lookupTable[] = {0x0,0x1b,0x36,0x2d,0x6c,0x77,0x5a,0x41}; +const char lookupTable1[] = {0x0,0x1b,0x2d,0x36,0x5a,0x41,0x77,0x6c}; + +//computes: +//c[i] = b[i] + factor * a[i] +//b[i] = c[i] +//a[i] = a[i] + c[i] +//THIS CODE IS IRREDUCIBLE SPECIFIC!!! +void Element::do_FFT_step(const Element& factor,Element* a, Element* b, const int len){ + register __m128i factor_reg = _mm_set1_epi64(*((__m64*)(&factor))); + register __m128i lookup_reg = _mm_loadl_epi64((__m128i*)lookupTable1); + + + + const size_t numIterations = len/2; + const bool hasTail = len % 2; + + for(size_t i=0; i< numIterations; i++){ + const size_t currIdx = i<<1; + + /* + * The following code loads two words of 64 bit each, + * name them a[0] and a[1] (without loss of generality), + * and compute in parallel (with vectorization) the values + * c[i] = b[i] + factor * a[i] + * b[i] = c[i] + * a[i] = a[i] + c[i] + * and stores them back to memory + */ + + // Load a[0] and a[1] + __m128i a_vec = _mm_loadu_si128((__m128i*)(&a[currIdx])); + + // Compute factor * a[i] for both i=0,1 + // This multiplication is done as binary polynomials, + // and results in products of degree at most 126. + // After this phase those products must be reduced modulu + // the field defining polynomial irr(x). + // This code is hard-coded for: + // irr(x) = x^64 + x^4 + x^3 + x + 1 + // + // After this operation the vectors t0,t1 contain those products where: + // t0[ 64: 0] = (factor * a[0]) mod x^64 + // t0[128:64] = (factor * a[0]) div x^64 + // t1[ 64: 0] = (factor * a[1]) mod x^64 + // t1[128:64] = (factor * a[1]) div x^64 + __m128i t0, t1; + t0=_mm_clmulepi64_si128(a_vec, factor_reg, 0); + t1=_mm_clmulepi64_si128(a_vec, factor_reg, 1); + + // Split the products to modular reminders and offsets: + // xmmRes [ 64: 0] = (factor * a[0]) mod x^64 + // xmmRes [128:64] = (factor * a[1]) mod x^64 + // xmmCarry0[ 64: 0] = (factor * a[0]) div x^64 + // xmmCarry0[128:64] = (factor * a[1]) div x^64 + // + // Important note : notice the degree of xmmCarry0[ 64: 0] and xmmCarry0[128:64] + // is at most (63+63)-64 = 62 + __m128i xmmRes, xmmCarry0, xmmCarry00, xmmCarry01, xmmCarry1; + xmmRes = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t0),_mm_castsi128_pd(t1),0)); + xmmCarry0 = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t0),_mm_castsi128_pd(t1),1|2)); + + // xmmCarry00[ 64: 0] = ( ( (factor * a[0]) div x^64 ) * ( 1 + x ) ) (mod x^64) + // xmmCarry00[128:64] = ( ( (factor * a[1]) div x^64 ) * ( 1 + x ) ) (mod x^64) + xmmCarry00 = _mm_xor_si128(xmmCarry0,_mm_slli_epi64(xmmCarry0,1)); + + // xmmCarry01[ 64: 0] = ( ( (factor * a[0]) div x^64 ) * ( x^3 + x^4 ) ) (mod x^64) + // xmmCarry01[128:64] = ( ( (factor * a[1]) div x^64 ) * ( x^3 + x^4 ) ) (mod x^64) + xmmCarry01 = _mm_xor_si128(_mm_slli_epi64(xmmCarry0,3),_mm_slli_epi64(xmmCarry0,4)); + + // xmmCarry1[ 64: 0] = ( ( (factor * a[0]) div x^64 ) * ( 1 + x + x^3 + x^4 ) ) (div x^64) + // xmmCarry1[128:64] = ( ( (factor * a[1]) div x^64 ) * ( 1 + x + x^3 + x^4 ) ) (div x^64) + // + // Important note : notice the degree of xmmCarry1[ 64: 0] and xmmCarry1[128:64] + // is at most (62+4)-64 = 2. + // In particular, their support is only in their 3 least significant bits. + xmmCarry1 = _mm_srli_epi64(xmmCarry0,60); + + // We use the fact the degree of either xmmCarr1[ 64: 0] and xmmCarry1[128:64] is at most 2, + // thus there are only 8 possibilities for each polynomial. + // Moreover, the product of any such option by 1 + x + x^3 + x^4 is a polynomial + // of degree oat most 2+4=6, + // and in particular, defined by it's first byte. + // We use a precomputed lookup table to compute the required reductions: + // + // xmmCarry1[ 64: 0] = ( ( ( (factor * a[0]) div x^64 ) * ( 1 + x + x^3 + x^4 ) (div x^64) ) * x^64 ) (mod Irr(x)) + // xmmCarry1[128:64] = ( ( ( (factor * a[1]) div x^64 ) * ( 1 + x + x^3 + x^4 ) (div x^64) ) * x^64 ) (mod Irr(x)) + xmmCarry1 = _mm_shuffle_epi8(lookup_reg,xmmCarry1); + + // Computing the reduced polynomial: + // + // xmmFinal[ 64: 0] = + // ( (factor * a[0]) mod x^64 ) + + // ( ( ( (factor * a[0]) div x^64 ) * ( 1 + x ) ) (mod x^64) ) + + // ( ( ( (factor * a[0]) div x^64 ) * ( x^3 + x^4 ) ) (mod x^64) ) + + // ( ( ( ( (factor * a[0]) div x^64 ) * ( 1 + x + x^3 + x^4 ) (div x^64) ) * x^64 ) (mod Irr(x))) + // = + // ( (factor * a[0]) mod x^64 ) + + // ( ( ( (factor * a[0]) div x^64 ) * ( 1 + x + x^3 + x^4 ) ) (mod x^64) ) + + // ( ( ( ( (factor * a[0]) div x^64 ) * ( 1 + x + x^3 + x^4 ) (div x^64) ) * x^64 ) (mod Irr(x))) + // = + // ( (factor * a[0]) mod x^64 ) + + // ( ( (factor * a[0]) div x^64 ) * ( 1 + x + x^3 + x^4 ) (mod Irr(x))) + // = + // (factor * a[0]) (mod Irr(x)) + // + // And similarly: + // xmmFinal[128:64] = (factor * a[1]) (mod Irr(x)) + __m128i tmp1 = _mm_xor_si128(xmmCarry00,xmmCarry01); + __m128i tmp2 = _mm_xor_si128(xmmCarry1,xmmRes); + __m128i xmmFinal = _mm_xor_si128(tmp1,tmp2); + + // Computing: b[i] = c[i] = b[i] + factor*a[i] + __m128i b_vec = _mm_loadu_si128((__m128i*)(&b[currIdx])); + b_vec = _mm_xor_si128(b_vec,xmmFinal); + //store b (case 17) + _mm_storeu_si128((__m128i*)(&b[currIdx]),b_vec); + + // Computing : a[i] = a[i] + c[i] = a[i] + b[i] + //store a (case 18) + a_vec = _mm_xor_si128(a_vec,b_vec); + _mm_storeu_si128((__m128i*)(&a[currIdx]),a_vec); + } + + if(hasTail){ + b[len-1] = b[len-1] + (factor * a[len-1]); + a[len-1] = a[len-1] + b[len-1]; + } + } + +void Element::c_mul(const Element* a, const Element* b, Element* c){ + //register __m128i l = _mm_loadu_si128((__m128i*)irr_poly_e[ord>>5]); + register __m128i t; + { + //copy a and b to a location where + //they can be copied from without accesing + //forbidden locations + Element arr[3]; + arr[0] = *a; + arr[1] = *b; + + t=_mm_clmulepi64_si128( + _mm_loadu_si128((__m128i*)&(arr[0])), + _mm_loadu_si128((__m128i*)&(arr[1])), + 0 + ); + } + + /* + *Matans original generic code + */ + //t=_mm_xor_si128(_mm_clmulepi64_si128(t,l,1),_mm_and_si128(t,mask)); + //t=_mm_xor_si128(_mm_clmulepi64_si128(t,l,1),t); + //memcpy(c,&t,sizeof(cell_t)*element_len); + + /* + * irreducible specific optimized code + */ + { + cell_t res0[2] __attribute__ ((aligned (16)));; + _mm_store_si128((__m128i*)res0,t); + const cell_t& lowDeg = res0[0]; + const cell_t& carry0 = res0[1]; + const cell_t carry1 = (carry0>>60); + const cell_t reducedCarry1 = lookupTable1[carry1]; + + const cell_t reducedCarry0 = carry0 ^ (carry0<<1) ^ (carry0<<3) ^ (carry0<<4); + + *(cell_t*)(c) = lowDeg ^ reducedCarry0 ^ reducedCarry1; + } + } + + +void Element::c_mul(const Element& a,const Element& b, Element& c){ + //__m128i l = _mm_loadu_si128((__m128i*)irr_poly_e[ord>>5]); + __m128i t; + + { + //copy a and b to a location where + //they can be copied from without accesing + //forbidden locations + Element arr[3]; + arr[0] = a; + arr[1] = b; + + t=_mm_clmulepi64_si128( + _mm_loadu_si128((__m128i*)&(arr[0])), + _mm_loadu_si128((__m128i*)&(arr[1])), + 0 + ); + } + + /* + *Matans original generic code + */ + //t=_mm_xor_si128(_mm_clmulepi64_si128(t,l,1),_mm_and_si128(t,mask)); + //t=_mm_xor_si128(_mm_clmulepi64_si128(t,l,1),t); + //memcpy(&c,&t,sizeof(cell_t)*element_len); + + /* + * irreducible specific optimized code + */ + { + cell_t res0[2] __attribute__ ((aligned (16)));; + _mm_store_si128((__m128i*)res0,t); + const cell_t& lowDeg = res0[0]; + const cell_t& carry0 = res0[1]; + const cell_t carry1 = (carry0>>60); + const cell_t reducedCarry1 = lookupTable1[carry1]; + + const cell_t reducedCarry0 = carry0 ^ (carry0<<1) ^ (carry0<<3) ^ (carry0<<4); + + *(cell_t*)(&c) = lowDeg ^ reducedCarry0 ^ reducedCarry1; + } + } + void Element::c_sqr(Element* a, Element* c){ + c_mul(a,a,c); + } + void Element::c_sqr(Element& a, Element& c){ + c_mul(a,a,c); + } + void Element::left_shift(cell_t * v, unsigned int k , unsigned int len){ + if(k>=(len<>log_bits_in_cell; + unsigned int par;// = uint_e_regs[2]; + par= k & andMask(log_bits_in_cell); + unsigned int rest;// = uint_e_regs[3]; + rest = bits_in_cell-par; + for(i = len - 1 ; i >= shift_cells+1 ; --i){ + v[i]=v[i-shift_cells]<>(rest-1))>>1; + } + v[shift_cells]=v[0]<0;--i) + v[shift_cells-1]=0; + } + void Element::egcd(cell_t a[element_len+1], cell_t b[element_len+1], cell_t g[element_len+1]){ + cell_t u[element_len+1]; + Element::assign_cells(u,a,element_len+1);//e_regs[0] + cell_t v[element_len+1]; + Element::assign_cells(v,b,element_len+1);//e_regs[1] + cell_t g1[element_len+1];//e_regs[2] + cell_t g2[element_len+1];//e_regs[3] + setZero(g2,element_len+1); + setUnit(g1,element_len+1);//g1 + while(!isUnit(u,element_len+1)){ + int i; //uint_e_regs[20] + int j; //uint_e_regs[21] + deg(u,i,element_len+1); + deg(v,j,element_len+1); + cell_t t[element_len+1];//e_regs[6] + if(ic,element_len);//=e_regs[8] + e[element_len]=0; + Element e_ord; + Element::generateOrdElement(&e_ord); + assign_cells(c,e_ord.c,element_len); + c[element_len]=1; + egcd(e,c,res->c); + } + void Element::c_setZero(Element* a){ + memset(a->c,0,sizeof(Element)); + } + void Element::c_setZero(Element& a){ + memset(a.c,0,sizeof(Element)); + } + void Element::c_setUnit(Element* a){ + c_setZero(a); + a->c[0]=1; + } + void Element::c_setUnit(Element& a){ + c_setZero(a); + a.c[0]=1; + } + bool Element::c_isUnit(Element* a){ + if(a->c[0]!=1) + return false; + for(unsigned int i = 1 ; i < element_len ; ++i) + if(a->c[i]!=0) + return false; + return true; + } + bool Element::c_isUnit(Element& a){ + if(a.c[0]!=1) + return false; + for(unsigned int i = 1 ; i < element_len ; ++i) + if(a.c[i]!=0) + return false; + return true; + } + +void Element::c_exp(Element a, unsigned long long exp, Element& res){ + Element::c_setUnit(res); + for(; exp != 0 ; exp>>=1){ + if(1&exp) + Element::c_mul(res,a,res); + Element::c_sqr(a,a); + } +} +void Element::vecXor(cell_t * a, cell_t * b, len_t len){ + for(unsigned int i = 0 ; i < len ; ++i) + a[i]^=b[i]; + } +void Element::vecXor(cell_t * a, cell_t * b, cell_t* c, len_t len){ + for(unsigned int i = 0 ; i < len ; ++i) + c[i]=(a[i]^b[i]); + } +void Element::printCells(const cell_t * x, len_t l){ + for(unsigned int i = l-1 ; i < (~0UL) ; --i){ + std::cout <(x[i]); + if(i!=0) + std::cout << ","; + } + +} +void Element::printElement(const Element& x){ + printCells(x.c,element_len); +} +void Element::assign(Element (*a),const Element (*b)){ + memcpy(a,b,sizeof(cell_t)*element_len); +} +void Element::assign(Element& a,const Element &b){ + memcpy(&a,&b,sizeof(cell_t)*element_len); +} + +void Element::c_vecMul(Element* a, Element* b, Element* c, len_t l){ + for(unsigned int i = 0 ; i < l ; ++i){ + Element::c_mul(a[i],b[i],c[i]); + } +} + +bool Element::c_isZero(Element& a){ + for(unsigned int i = 0 ; i < element_len ; ++i) + if(a.c[i]!=0) + return false; + return true; +} +void Element::naiveMul(const cell_t* a, const cell_t* b, cell_t* c){ + cell_t clmul[element_len<<1]; + for(unsigned int i = 0 ; i < (element_len<<1) ; ++i) + clmul[i]=0; + for(unsigned int i = 0 ; i < ord ; ++i) + for(unsigned int j = 0 ; j < ord ; ++j){ + if(EXTRACT_BIT(a,i) && + EXTRACT_BIT(b,j) ) + clmul[(i+j)>>log_bits_in_cell]^=(((cell_t)1)<<((i+j) & andMask(log_bits_in_cell))); + } + for(unsigned int i = (ord<<1)-1 ; i>=ord ; --i){ + if(EXTRACT_BIT(clmul,i)){ + for(unsigned int j = 0 ; j < mod_len[ord>>5];++j) + TOGGLE_BIT(clmul,i-ord + irr_poly_index[ord>>5][j]); + } + } + unsigned int i; + for(i = 0 ; i < element_len ; ++i) + c[i]=clmul[i]; +} + +void Element::naiveMul(const Element& a, const Element& b, Element& c){ + naiveMul(a.c,b.c,c.c); +} +} /* namespace FFF */ diff --git a/algebra/FFT/src/Element.h b/algebra/FFT/src/Element.h new file mode 100644 index 0000000..0a42943 --- /dev/null +++ b/algebra/FFT/src/Element.h @@ -0,0 +1,235 @@ +/* + * Element.h + * + * Created on: 2 ���� 2014 + * Author: hamilis + */ + +/* + * This is the header file of the elements as they are represented on CPU. + * Each element is just an array of cell_t, the name of the attribute it c, so e.c will return that array that represents element c. + * As for now, cell_t is long unsigned int, therefore it is 8 bytes so elements in GF(2^64) will be exactly in one. + * Given Element e, denote by e_i the i-th bit (starting from LSB, starting from entry 0 in the array) in e.c. + * So the polynomial that e represents in the finite field of GF(2^64) is: + * Sum(i=0 to 63) { e_i * x^i }. + */ +#ifndef ELEMENT_H_ +#define ELEMENT_H_ +#include "Definitions.h" +namespace FFF{ + +// When changing be sure to update "log_bits_in_cell". +typedef long long unsigned int cell_t; + +class Element { + //Attributes +public: + static const unsigned int log_bits_in_byte = 3; + static const unsigned int bits_in_byte = 1<>log_bits_in_byte)/sizeof(cell_t); +/* + * http://www.hpl.hp.com/techreports/98/HPL-98-135.pdf - List of irreducible binary + * polynomials. + * All of these polynomials are either trinomials or pentanomials. + * Moreover they are of the form x^d+x^j+...+1 where: + * + * j*(j-1)*(j-2)/6 <= d + */ +static const idx_t irr_poly_index[][max_nonzero_coefs_in_mod]; +static const cell_t irr_poly_e[][element_len]; + +/* + * mod_len[i] is the number of non-zero coefficients in the i-th irreducible polynomial + */ +static const len_t mod_len[]; + +private: +/* + * Input: + * 1) a - pointer to array of cell_t represents an element. + * 2) i - Will contain the degree of the polynomial represented by a. + * 3) len - length of array of a. + */ + static void deg(cell_t * a, int & i, len_t len); + /* + * Left shifts array pointed by v of length len by k bits to the left (from LSB to MSB). + * k - can be larger than the number of bits in a single cell. + */ + static void left_shift(cell_t * v, unsigned int k , unsigned int len); + /* + * Given to binary polynomials a,b writes in g the egcd polynomial of a and b, using the extended euclidian algorithm. + */ + static void egcd(cell_t a[element_len+1], cell_t b[element_len+1], cell_t g[element_len+1]); +public: + + cell_t c[element_len]; + + /* + * The c'tor does nothing. + */ + Element(){}; + /* + * Copy c'tor. + */ + Element(const Element& e); + /* + * Assignment operator. + */ + void operator=(const Element& e); + /* + * D'tor, there is nothing to deallocate. + */ + ~Element(){}; + /* + * Denote by r(x) the irreducible polynomial that defines the field GF(2^64) therefore r(x) = x^64 + s(x). + * This function assigns s(x) to e. + */ + static void generateOrdElement(Element* e); + /* + * This function performs a carryless multiplication of a and b, and XORs thee result to res. + */ + static void clmulXor(const cell_t* a,const cell_t* b, cell_t* res); + static void reduce(cell_t* clmul_res, cell_t* res); + static void clmul(const cell_t* a,const cell_t* b, cell_t* res); + +/***** Constant Memory Settings *****/ + /* + * This function is used for the GPU implementation and sets the relevant irreducible polynomials at the constant memory. + */ + static void setMod(); +/***** Chunk/Vector Operations*****/ + /* + * return true iff elements a and b are equal. + */ + static bool equals(Element* a, Element* b); + static bool equals(Element& a, Element& b); + +/***** CPU Operations *****/ + /* + * a[i] = a[i]*e. + */ + static void setElementMul(Element* e); + + /* + * writes in c the addition of elements a and b. + * c = a + b. + */ + static void c_add(const Element* const a,const Element* const b, Element* const c); + static void c_add(const Element& a, const Element& b, Element& c); + /* + * writes in c the multiplication of elements a and b. + * c = a * b. + */ + static void c_mul(const Element* a,const Element* b,Element* c); + static void c_mul(const Element& a,const Element& b, Element& c); + /* + * XORs to c the multiplication of a and b. + * c ^= (a*b) + */ + static void c_mulXor(const Element& a,const Element& b,Element& c); + + //computes: + //c[i] = b[i] + factor * a[i] + //b[i] = c[i] + //a[i] = a[i] + c[i] + //THIS CODE IS IRREDUCIBLE SPECIFIC!!! + static void do_FFT_step(const Element& factor,Element* a, Element* b, const int len); + + /* + * squares elements a in c. + * c = (a^2). + */ + static void c_sqr(Element* a, Element* c); + static void c_sqr(Element& a, Element& c); + /* + * Exponentiates element a by exp and writes the result into res. + * res = (a^exp). + */ + static void c_exp(Element a, unsigned long long exp, Element& res); + /* + * Inverts a and writes the result in c. + * c = a ^ (-1). + */ + static void c_inv(Element* a, Element* c); + static void c_inv(Element& a, Element& res); + /* + * a = 0. + */ + static void c_setZero(Element* a); + static void c_setZero(Element& a); + /* + * Returns true iff a is zero element. + */ + static bool c_isZero(Element& a); + /* + * a = 1. + */ + static void c_setUnit(Element* a); + static void c_setUnit(Element& a); + /* + * Returns true iff a is the unit element of the field. + */ + static bool c_isUnit(Element* a); + static bool c_isUnit(Element& a); + /* + * a,b,c - arrays of elements of length l. + * c [i] = a[i] * b[i]. + */ + static void c_vecMul(Element* a, Element* b, Element* c, len_t l); + /* + * a, b - arrays of length l. + * a[i] ^= b[i]. + */ + static void vecXor(cell_t * a, cell_t * b, len_t len); + /* + * a,b,c - arrays of length l. + * c[i] = a[i] ^ b[i]. + */ + static void vecXor(cell_t * a, cell_t * b, cell_t* c, len_t len); + /* + * a,b - arrays of length len. + * a[i] = b[i]. + */ + static void assign_cells(cell_t* a, cell_t* b,len_t len); + /* + * Element pointed by a is set to b. + */ + static void assign(Element (*a),const Element (*b)); + static void assign(Element& a,const Element &b); + /**** Testing and Representation ****/ + /* + * prints cells pointed by x of length l. + */ + static void printCells(const cell_t * x, len_t l); + /* + * Prints the element x. + */ + static void printElement(const Element& x); + /* + * This is the implementation of the naive multiplication method over finite fields, just for comparison. + */ + static void naiveMul(const cell_t* a, const cell_t* b, cell_t* c); + static void naiveMul(const Element& a, const Element& b, Element& c); + + + Element operator+(const Element& e) const + { + Element res; + Element::c_add(Element(*this),e,res); + return res; + } + + Element operator*(const Element& e) const + { + Element res; + Element::c_mul(Element(*this),e,res); + return res; + } +}; +}; + +#endif /* ELEMENT_H_ */ diff --git a/algebra/FFT/src/FFT.cpp b/algebra/FFT/src/FFT.cpp new file mode 100644 index 0000000..5267afe --- /dev/null +++ b/algebra/FFT/src/FFT.cpp @@ -0,0 +1,610 @@ +/* + * FFT.cpp + * + * Created on: Jul 2, 2014 + * Author: matan + */ +#include "FFT.h" +#include "GPU_FFT.cuh" +#include +#include +#include +#include +namespace FFF { +/* + * Internal Function Definitions + */ +/* + * This function generates basis G from basis B according to the Gao & Mateer's FFT algorithm. + */ +void GFromB(Element* G, Element* B, len_t g_l, Element& g_shift, Element& b_shift) +{ + Element::c_inv(B[g_l],B[g_l]); + for(unsigned int i = 0 ; i < g_l ; ++i){ + Element::c_mul(B[i],B[g_l],G[i]); + } + Element::c_mul(b_shift,B[g_l],g_shift); +} +/* + * This function generates basis D from basis G according to the Gao & Mateer's FFT algorithm. + */ +void DFromG(Element* D, Element* G, len_t d_l, Element& d_shift, Element& g_shift){ + for(unsigned int i = 0 ; i < d_l ; ++i){ + Element::c_sqr(G[i],D[i]); + Element::c_add(G[i],D[i],D[i]); + } + Element::c_sqr(g_shift,d_shift); + Element::c_add(g_shift,d_shift,d_shift); +} + +void FFT::changeShift(const Element& newShift){ + Element* D = (Element*)malloc(sizeof(Element)*basis.getSize()); + Element* G = (Element*)malloc(sizeof(Element)*basis.getSize()); + Element g_shift; + Element d_shift; + int b_size = basis.getSize(); + d_shift = newShift; + basis.getBasis(D); + + for(unsigned int j = b_size-1 ; j > 0 ; --j ) + { + GFromB(G,D,j,g_shift,d_shift); + updateShiftSubspaceElements_cpu(g_shift,j,subspaces[b_size-1-j]); + DFromG(D,G,j,d_shift,g_shift); + } + this->lastD = D[0]; + Element::c_inv(lastD,i_lastD); + this->lastShift = d_shift; + free(D); + free(G); + + basis.getShift(d_shift); +} + +FFT::FFT(Basis& b, const fft_operation_t operation) : + basis(b), bases(new Basis[b.getSize()-1]), + bms((Element*)malloc(sizeof(Element)*(b.getSize()-1))), + i_bms((Element*)malloc(sizeof(Element)*(b.getSize()-1))), + exps((Element**)malloc(sizeof(Element*)*(b.getSize()-1))), + subspaces((Element**)malloc(sizeof(Element*)*(b.getSize()-1))) +#ifdef __GPU + ,gpu_exp((Chunk**)malloc(sizeof(Chunk*)*(b.getSize()-1))) + ,gpu_i_exp((Chunk**)malloc(sizeof(Chunk*)*(b.getSize()-1))) + ,gpu_subspace((Chunk**)malloc(sizeof(Chunk*)*(b.getSize()-1))) +#endif // #ifdef __GPU +{ + omp_set_num_threads(omp_max_threads); + Element* D = (Element*)malloc(sizeof(Element)*b.getSize()); + Element* G = (Element*)malloc(sizeof(Element)*b.getSize()); +#ifdef __GPU + Elements_Chunk E; +#endif // #ifdef __GPU + Element g_shift; + Element d_shift ; + int b_size = b.getSize(); + for (int i = 0; i < b_size - 1; ++i){ + exps[i] = (Element*)malloc(sizeof(Element)*(1ULL << (b_size - i))); + subspaces[i] = (Element*)malloc(sizeof(Element)*(1ULL << (b_size - i - 1))); +#ifdef __GPU + gpu_exp[i] = (Chunk*)malloc(sizeof(Chunk)*(1UL<<(MAX((int)b_size-i-(int)Chunk::log_elements_in_chunk,0)))); + gpu_i_exp[i] = (Chunk*)malloc(sizeof(Chunk)*(1UL<<(MAX((int)b_size-i-(int)Chunk::log_elements_in_chunk,0)))); + gpu_subspace[i] = (Chunk*)malloc(sizeof(Chunk)*(1UL<<(MAX((int)b_size-i-1-(int)Chunk::log_elements_in_chunk,0)))); +#endif // #ifdef __GPU + } + b.getShift(d_shift); + b.getBasis(D); + + for(unsigned int j = b_size-1 ; j > 0 ; --j ) + { + bms[b_size-1-j]=D[j]; + Element::c_inv(D[j],i_bms[b_size-1-j]); + if(operation == FFT_OP){ + multiExponentiate_cpu(bms[b_size-1-j],1UL<<(j+1),exps[b_size-1-j]); + } + else{ //operation == IFFT_OP + multiExponentiate_cpu(i_bms[b_size-1-j],1UL<<(j+1),exps[b_size-1-j]); + } +#ifdef __GPU + if(j+1 <= Chunk::log_elements_in_chunk){ + for(size_t i = 0 ; i < (1UL<<(Chunk::log_elements_in_chunk-(j+1))); ++i){ + memcpy(&(E.e[i<<(j+1)]),exps[b_size-j-1],sizeof(Element)*(1UL<<(j+1))); + } + Chunk::normalToChunk(&E,gpu_exp[b_size-j-1],1,true); + } else { + Chunk::normalToChunk((Elements_Chunk*)(exps[b_size-1-j]),gpu_exp[b_size-j-1],1UL<<(j+1-Chunk::log_elements_in_chunk),true); + } +#endif // #ifdef __GPU +#ifdef __GPU + if(j+1 <= Chunk::log_elements_in_chunk){ + for(size_t i = 0 ; i < (1UL<<(Chunk::log_elements_in_chunk-(j+1))); ++i){ + memcpy(&(E.e[i<<(j+1)]),i_exps[b_size-j-1],sizeof(Element)*(1UL<<(j+1))); + } + Chunk::normalToChunk(&E,gpu_i_exp[b_size-j-1],1,true); + } else { + Chunk::normalToChunk((Elements_Chunk*)i_exps[b_size-1-j],gpu_i_exp[b_size-j-1],1UL<<(j+1-Chunk::log_elements_in_chunk),true); + } +#endif // #ifdef __GPU + GFromB(G,D,j,g_shift,d_shift); + bases[b_size-1-j]=Basis(G,j,g_shift); + generateSubspaceElements_cpu(G,g_shift,j,subspaces[b_size-1-j]); +#ifdef __GPU + if(j>=Chunk::log_elements_in_chunk) + Chunk::normalToChunk((Elements_Chunk*)subspaces[b_size-1-j],gpu_subspace[b_size-1-j],1UL<<(j-Chunk::log_elements_in_chunk),true); + else + { + for(size_t i =0 ; i < 1UL<<(j) ; ++i) + for(size_t k = 0 ; k < (Chunk::elements_in_chunk>>(j));++k){ + if(!(k&1)) + Element::c_setZero(E.e[(k<<(j))+i]); + else + E.e[(k<<(j))+i] = subspaces[b_size-1-j][i]; + } + + Chunk::normalToChunk(&E,gpu_subspace[b_size-1-j],1,true); + } +#endif // #ifdef __GPU + DFromG(D,G,j,d_shift,g_shift); + } + this->lastD = D[0]; + Element::c_inv(lastD,i_lastD); + this->lastShift = d_shift; +#ifdef __GPU + for(size_t i = 0 ; i < (Chunk::elements_in_chunk) ; i+=2){ + E.e[i] = lastShift; + Element::c_add(lastShift,lastD,E.e[i+1]); + } + Chunk::normalToChunk(&E,&linear_mul,1,true); + E.e[1]=i_lastD; + Element::c_mul(E.e[1],lastShift,E.e[0]); + for(size_t i = 2 ; i < (Chunk::elements_in_chunk) ; i+=2){ + E.e[i]=E.e[0]; + E.e[i+1]=E.e[1]; + } + Chunk::normalToChunk(&E,&ilinear_mul,1,true); +#endif // #ifdef __GPU + free(D); + free(G); +} + +FFT::~FFT() { + delete[] bases; + free(bms); + free(i_bms); + for(unsigned int i = 0 ; i < this->basis.getSize()-1 ; ++i){ + free(exps[i]); + free(subspaces[i]); +#ifdef __GPU + free(gpu_exp[i]); + free(gpu_i_exp[i]); + free(gpu_subspace[i]); +#endif // #ifdef __GPU + } +#ifdef __GPU + free(gpu_exp); + free(gpu_i_exp); + free(gpu_subspace); +#endif // #ifdef __GPU + free(exps); + free(subspaces); +} + + /* + * Non-allocating version of the FFT Algorithm. (Ariel:does allocate if basis size larger than p_len) + * + */ +void FFT::AlgFFT(Polynomial* P, len_t p_len)const{ + Element* c = *P; + len_t eval_len = 1UL<basis.getSize(); + if(p_len > eval_len) + return; + else if(p_len < eval_len){ + c =(Element*) malloc(sizeof(Element)*(eval_len)); + memset(c,0,sizeof(Element)*eval_len); + memcpy(c,*P,sizeof(Element)*p_len); + free(*P); + *P = (Polynomial)c; + } +#ifdef __GPU + GPU_FFT::fft_gpu(this,(Element**)P); +#else // #ifdef __GPU + this->fft_cpu(*P); +#endif // #ifdef __GPU + // Use this for debug, print out of the polynomial +// std::cout << "here in Matan's ALGFFT:" << std::endl; +// Polynomials::printPolynomial(*P,8);//temp added for debuging ARIEL + } + +void FFT::AlgIFFT(Element* P){ +#ifdef __GPU + this->ifft_cpu(P); +// this->fft_gpu(P); +#else // #ifdef __GPU + this->ifft_cpu(P); +#endif // #ifdef __GPU + + } + /* + * Allocating Version of the FFT Algoirthm + */ + void FFT::AlgFFT(const Polynomial* P, len_t p_len, Element** res){ + if(p_len > (1UL<basis.getSize())) + return; + else if(p_len < (1UL<basis.getSize())){ + free(*res); + *res = (Element*) malloc(sizeof(Element)*(1UL<basis.getSize())); + } + memset(res,0,sizeof(Element)*(1UL<basis.getSize())); + memcpy(res,*P,sizeof(Element)*p_len); + AlgFFT(res,(1UL<basis.getSize())); + } + + void FFT::AlgIFFT(Element* P, Polynomial* res){ + len_t p_len = 1UL<=0 ; --i){ +#pragma omp parallel for + for(idx_t j=0 ; j < (1UL<>1)+(i>>1)]; + G[i]=res[i>>1]; + } + } + void FFT::GPartition_cpu(Element* G, len_t log_g_len, Element* res)const{ + len_t g_len = 1<>1]=G[i]; + res[(g_len>>1)+(i>>1)]=G[i+1]; + } + } + void FFT::iGPartition_cpuOMP(Element* G, len_t log_g_len,Element* res){ + len_t g_len = 1<>1]; + G[i+1]=res[(g_len>>1)+(i>>1)]; + } + } + void FFT::GPartition_cpuOMP(Element* G, len_t log_g_len,Element* res)const{ + len_t g_len = 1<>1]=G[i]; + res[(g_len>>1)+(i>>1)]=G[i+1]; + } + } + void FFT::UVFromW_cpu_serial(Polynomial P, len_t log_g_len, len_t size){ + Polynomial G; + Element* t = subspaces[this->basis.getSize()-log_g_len]; + len_t half_g_len = 1UL<<(log_g_len-1); + for(idx_t j = 0; j<(1UL<= omp_max_threads) + { +#pragma omp parallel for private(G,tid) + for(idx_t j = 0; j<(1UL<basis.getSize()-log_g_len]; + len_t half_g_len = 1UL<<(log_g_len-1); + for(idx_t j = 0; j<(1UL<= omp_max_threads) + { +#pragma omp parallel for private(G,tid) schedule(static,1) + for(idx_t j = 0; j<(1UL<basis.getSize(); + Element* p_cpy = NULL; + Element* it; + Element* private_it; + for(idx_t i = size ; i>1 ; --i){ + UVFromW_cpu_serial(P,i,size); + } + for(idx_t i = 1 ; i < (1UL<= size - min_log_general_level+1 ; --i){ + UVFromW_cpu(P,i,size); + } + if(min_log_general_level > size-1){ // Can be deleted +#pragma omp parallel for schedule(static,1) //MAX + for(idx_t i = 1 ; i < (1UL< (1UL< (1UL<basis.getSize(); + Element* p_cpy = P; + Element* it; + Element* private_it; + for(idx_t i =size; i>1 ; --i){ + if((size-i)&1UL){ + it=c_p_cpy; + p_cpy=P; + } else{ + it=P; + p_cpy=c_p_cpy; + } + for( size_t j = 0; j < (1UL<<(size-i)) ; ++j) + { + private_it = it+ j*(1UL<size-min_log_general_levels ; --i){ + if((size-i)&1){ + it=c_p_cpy_local; + p_cpy=P; + } else{ + it=P; + p_cpy=c_p_cpy_local; + } + idx_t mask = andMask(i); +#pragma omp parallel for schedule(guided) + for(idx_t k = 0 ; k < (1UL< size-1){ +#pragma omp parallel for schedule(static,(1UL<<(size-1))/omp_max_threads)//MAX + for (unsigned long long i = 1; i <= (1UL << size); i += 2){ + Element::c_mulXor(P[i],lastShift,P[i-1]); + Element::c_mul(P[i],lastD,P[i]); + Element::c_add(P[i],P[i-1],P[i]); + } + } + for(idx_t i = size-min_log_general_levels+1; i<= size ; ++i){ + WFromUV_cpu(P,i,size); + } + } +} /* namespace FFF */ diff --git a/algebra/FFT/src/FFT.h b/algebra/FFT/src/FFT.h new file mode 100644 index 0000000..6a3f395 --- /dev/null +++ b/algebra/FFT/src/FFT.h @@ -0,0 +1,174 @@ +/* + * FFT.h + * + * Created on: Jul 2, 2014 + * Author: matan + */ + +/* + * This file is the API for the FFT algorithms. + */ +#ifndef FFT_H_ +#define FFT_H_ + +#include "Basis.h" +#ifdef __GPU +#include "Chunk.cuh" +#include +#include +#endif // #ifdef __GPU +#include "Definitions.h" +#include "Polynomials.h" +#include "Element.h" + +namespace FFF { +/* + * This class is for the FFT algorithm. + * When creating an instance of the class, it gets as input a basis for the FFT over which the polynomial (that will be given later) will be evaluated + * and preprocesses some of the data needed for it. + */ +typedef enum{FFT_OP,IFFT_OP} fft_operation_t; +class FFT { +public: + /* + * This is the basis over which the polynomial will be evaluated, could be affine. + */ + Basis basis; + /* + * This is the series of bases over which the polynomial is actually recusrively evaluated. + */ + Basis* bases; + /* + * This is an array of the last element in each of the bases that evaluated. + */ + Element* bms; + /* + * This is an array containing the inverse of the elements in bms. + */ + Element* i_bms; + /* + * This is the elements that spans the last subspace to evaluate at the bottom of the recursion. + * This is an affine subspace of dimension 1. + */ + Element lastD; + /* + * This is the inverse of lastD. + */ + Element i_lastD; + /* + * This is the shift of the last affine subspace. + */ + Element lastShift; + /* + * This is a two-dimensional array s.t. exps[i] will contain a multiexponentiation of the i-th element bms up the needed degree + * (2^ (sizeOfOriginalBasis-i))-1. + */ + Element** exps; + /* + * This are the subspaces needed to multiply at the WFromUV and UVFromW phases. + */ + Element** subspaces; +#ifdef __GPU + /* + * These are the chunks used to calculate the FFT on GPU. + */ + Chunk** gpu_exp; + Chunk** gpu_i_exp; + /* + * This is the chunk that is used for the last phase, every even element in the chunk contains lastD+lastShift, every odd one will contains only lastD. + */ + Chunk linear_mul; + Chunk ilinear_mul; + /* + * These are the subspaces needed for the GPU phase of WFromUV. + */ + Chunk** gpu_subspace; +#endif + /* + * Gets a polynomial P and evaluates the FFT over P. + */ + void fft_cpu(Polynomial P)const; + /* + * This is the serial version of the FFT. + * log_size is log the numebr of processors. + * c_p_cpy is an auxilliary free space in memory. + */ + void fft_serial(Polynomial P,len_t log_size, Element* c_p_cpy)const; + /* + * Gets a polynomial P and evaluated the iFFT over P. + */ + void ifft_cpu(Polynomial P); + /* + * This is the serial version of the iFFT. + * log_size is log the numebr of processors. + * c_p_cpy is an auxilliary free space in memory. + */ + void ifft_serial(Polynomial P,len_t log_size, Element* c_p_cpy); + /* + * Calculates e^0,e^1,...,e^(t-1). + * Returns them in an array. + * The array has to be unallocated by the user using "free()". + */ + void multiExponentiate_cpu(Element& e, len_t t, Element* res); + /* + * GPartition phase on CPU. + */ + void GPartition_cpu(Element* G, len_t log_g_len,Element* res)const; + /* + * The inverse of the partition on cpu. + */ + void iGPartition_cpu(Element* G, len_t log_g_len,Element* res); + /* + * This is an implementation of the partition phase with openMP, relevant for upper levels of the FFT. + */ + void GPartition_cpuOMP(Element* G, len_t log_g_len,Element* res)const; + /* + * This is an implementation of the inverse partition phase with openMP, relevant for upper levels of the iFFT. + */ + void iGPartition_cpuOMP(Element* G, len_t log_g_len,Element* res); + /* + * Parallel WFromUV implementation. + */ + void WFromUV_cpu(Polynomial P, len_t log_g_len, len_t size)const; + /* + * Serial WFromUV implementation - for lower levels of FFT. + */ + void WFromUV_cpu_serial(Polynomial P, len_t log_g_len, len_t size)const; + /* + * Parallel UVFromW implementation. + */ + void UVFromW_cpu(Polynomial P, len_t log_g_len, len_t size); + /* + * Serial UVFromW implementation - for lower levels of iFFT. + */ + void UVFromW_cpu_serial(Polynomial P, len_t log_g_len, len_t size); +#ifdef __GPU + void fft_gpu(Polynomial P); +#endif // #ifdef __GPU + /* + * This function used at preprocessing level to generate all elements of a subspaces. + * Given array b of length b_len , writes in res[i] the i-th element of the subspace spanned by b with shift s. + * + * This function doesn't check the elements in b are linearly independent. + */ +static void generateSubspaceElements_cpu(const Element* b,const Element& s,const len_t b_len,Element* const res); +static void updateShiftSubspaceElements_cpu(const Element& s,const len_t b_len,Element* const res); + FFT(Basis& b,const fft_operation_t operation); + virtual ~FFT(); + + void changeShift(const Element& newShift); + + /* + * Non-allocating version of the i/FFT Algorithms. + */ + void AlgFFT(Polynomial* P, len_t p_len)const; + void AlgIFFT(Element* P); + /* + * Allocating Version of the i/FFT Algoirthms, where result is not written in place but it written to *res (you don't have to malloc *res). + */ + void AlgFFT(const Polynomial* P, len_t p_len, Element** res); + void AlgIFFT(Element* P, Polynomial* res); +}; + void fft_gpu_aux(FFT* fft, Polynomial P); +} /* namespace FFF */ +#endif /* FFT_H_ */ diff --git a/algebra/FFT/src/FFT_cuda.cu b/algebra/FFT/src/FFT_cuda.cu new file mode 100644 index 0000000..7890871 --- /dev/null +++ b/algebra/FFT/src/FFT_cuda.cu @@ -0,0 +1,15 @@ +/* + * FFT.cu + * + * Created on: Jul 30, 2014 + * Author: matan + */ +#include "FFT.h" +const unsigned int max_fft_dim = 27; //Should someday get bigger... hardware dependent. + + +namespace FFF{ +} + + + diff --git a/algebra/FFT/src/GPU_FFT.cu b/algebra/FFT/src/GPU_FFT.cu new file mode 100644 index 0000000..4089c45 --- /dev/null +++ b/algebra/FFT/src/GPU_FFT.cu @@ -0,0 +1,2038 @@ +/* + + * GPU_FFT.cu + * + * Created on: Aug 26, 2014 + * Author: matan + */ +#include "GPU_FFT.cuh" +#include "Polynomials.h" +#include "Definitions.h" +#include +#ifdef __GNUC__ +#include +#endif //#ifdef __GNUC__ +#include +#include +//#include "Tests.h" +#include "Chunk.cuh" +#include +#include +#include + +using namespace std; +#ifdef __GPU +namespace FFF{ +/*------- CHUNK ------*/ +/* + * Chunk constant memory + */ +__constant__ idx_t p_Mod[max_nonzero_coefs_in_mod]; +__constant__ idx_t p_ModLen; +__constant__ Element element_mul; +__constant__ Chunk c; + +/* + * FFT Constant Memory + */ +__constant__ chunk_cell_t d_chunk_cell_mask[Chunk::log_elements_in_chunk+2]; +__constant__ chunk_cell_t d_partition[1<>1) +__device__ void a_chunkToNormal(Chunk *d_a, Elements_Chunk *d_b, idx_t idx) +{ + cell_t ans = 0; + idx_t element_idx = idx & andMask(Chunk::log_elements_in_chunk); + idx_t cell_idx = idx >> Chunk::log_elements_in_chunk; + for(unsigned int i = cell_idx<v[i])>>(element_idx))&1))<<(i-(cell_idx<e[element_idx].c[cell_idx]=ans; +} +__global__ void k_chunkToNormal(Chunk *d_a,Elements_Chunk *d_b , len_t len) +{ + const unsigned int threads_in_chunk = Chunk::elements_in_chunk * Element::element_len; + __shared__ Chunk input[max_block_size / threads_in_chunk]; + idx_t idx = threadIdx.x + blockDim.x*blockIdx.x; + if(idx >= len*threads_in_chunk) + return; + idx_t chunkIdx = (idx) / (Element::element_len*Chunk::elements_in_chunk); + idx_t in_chunkIdx = (idx & (Element::element_len * Chunk::elements_in_chunk - 1)); + idx_t chunks_in_block = blockDim.x / Chunk::cells_in_chunk; + idx_t inBlockChunkIdx = chunkIdx & (threads_in_chunk-1); + for(unsigned int i = 0 ; i < sizeof(cell_t)/sizeof(chunk_cell_t) ; ++i){ + input[inBlockChunkIdx].v[in_chunkIdx + i*threads_in_chunk] = d_a[chunkIdx].v[in_chunkIdx+i*threads_in_chunk]; + } + a_chunkToNormal(&(input[inBlockChunkIdx]), &(d_b[chunkIdx]),in_chunkIdx); +} +__host__ void Chunk::chunkToNormal(Chunk(*h_a), Elements_Chunk(*h_b), len_t len, bool copy) +{ + //Declare device variables + Chunk (*d_a); + Elements_Chunk (*d_b); + + const unsigned int num_element = len*elements_in_chunk; + const unsigned int threads = Element::element_len * num_element; + + //Define Block and Grid Size. + dim3 blockSize(max_block_size,1,1); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + if(copy){ + //Allocate Memory on GPU. (global) + cudaMalloc(&d_a,sizeof(Chunk)*len); + cudaMalloc(&d_b,sizeof(Elements_Chunk)*len); + + //Copy memory to GPU. + cudaMemcpy(d_a,h_a,sizeof(Chunk)*len,cudaMemcpyHostToDevice); + } else { + d_a = h_a; + d_b = h_b; + } + + //Launch Kernel + k_chunkToNormal<<>>(d_a,d_b,len); + if(copy){ + //Copy results back to memory + cudaMemcpy(h_b,d_b,sizeof(Elements_Chunk)*len,cudaMemcpyDeviceToHost); + + //Free allocated memory. + cudaFree(d_a); + cudaFree(d_b); + } +} +__device__ void a_normalToChunk(Elements_Chunk *d_a, Chunk *d_b, idx_t idx) +{ + chunk_cell_t ans = 0; + idx_t cell_idx = idx>>Element::log_bits_in_cell; + for(unsigned int i = 0 ; i < Chunk::elements_in_chunk ; ++i) + ans^=((((d_a->e[i].c[cell_idx])>>(idx& andMask(Element::log_bits_in_cell)))&1)<v[idx]=ans; +} +__global__ void k_normalToChunk(Elements_Chunk *d_a,Chunk *d_b , len_t len) +{ + idx_t idx = threadIdx.x + blockDim.x*blockIdx.x; + if(idx >= (len<> Chunk::log_cells_in_chunk; + idx_t in_chunkIdx = (idx & andMask(Chunk::log_cells_in_chunk)); + a_normalToChunk(&(d_a[chunkIdx]),&(d_b[chunkIdx]),in_chunkIdx); +} +__host__ void Chunk::normalToChunk(Elements_Chunk(*h_a), Chunk (*h_b), len_t len,bool copy) +{ + + //Declare device variables + Elements_Chunk (*d_a); + Chunk (*d_b); + + const unsigned int threads = len<>>(d_a,d_b,len); + + //Copy results back to memory + if(copy){ + cudaMemcpy(h_b,d_b,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); + //Free allocated memory. + cudaFree(d_a); + cudaFree(d_b); + } + +} +__host__ void Chunk::setMod(){ + cudaMemcpyToSymbol(p_Mod,&(Element::irr_poly_index[ord>>log_warp_size]),sizeof(idx_t)*max_nonzero_coefs_in_mod); + cudaMemcpyToSymbol(p_ModLen,&(Element::mod_len[ord>>log_warp_size]),sizeof(idx_t)); +} +//__device__ void Chunk::chunk_reduce_xor(Chunk *a, Chunk *c_bottom, Chunk*c_top, idx_t idx) +//{ +// chunk_cell_t ans=c_bottom->v[idx]; +// unsigned int temp_idx; +// for(idx_t i = 0 ; i < p_ModLen ; ++i) +// { +// for(idx_t j = 0 ; j < p_ModLen ; ++j) +// { +// temp_idx = idx+(ord<<1)-p_Mod[i]-p_Mod[j]; +// if(temp_idx >= (ord<<1)-p_Mod[j] && temp_idx < (ord<<1)) +// ans^=c_top->v[temp_idx-ord]; +// } +// } +// a->v[idx]^=ans; +//} +__device__ void Chunk::chunk_xor(Chunk *a, Chunk* b, idx_t idx){ + a->v[idx]^=b->v[idx]; +} +__device__ void Chunk::chunk_reduce_xor(Chunk *a, Chunk *c_bottom, idx_t idx,Chunk* to_xor ,int shift) +{ + // replaced p_ModLen-1 by 4 + for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) +#pragma unroll + for(unsigned int j = 0 ; j < 4 ; ++j) + { + c_bottom->v[(ord>>1)+idx+i+p_Mod[j]]^=c_bottom->v[(ord>>1)+ord+idx+i]; + } + for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) +#pragma unroll + for(unsigned int j = 0 ; (j) < 4 ; ++j) + { + c_bottom->v[idx+i+p_Mod[j]]^=c_bottom->v[ord+idx+i]; + } + for(unsigned int i = 0 ; i < ord ; i+=warp_size){ + to_xor->v[idx+i]^=(c_bottom->v[idx+i]>>shift); + } +} + +__device__ void Chunk::chunk_reduce(Chunk *a, Chunk *c_bottom, idx_t idx) +{ + //replaced p_ModLen with 5 + for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) + for(unsigned int j = 0 ; j+1 < 5 ; ++j) + { + c_bottom->v[(ord>>1)+idx+i+p_Mod[j]]^=c_bottom->v[(ord>>1)+ord+idx+i]; + } + for(unsigned int i = 0 ; i < (ord>>1); i+=warp_size) + for(unsigned int j = 0 ; (j+1) < 5 ; ++j) + { + c_bottom->v[idx+i+p_Mod[j]]^=c_bottom->v[ord+idx+i]; + } + for(unsigned int i = 0 ; i < ord ; i+=warp_size){ + a->v[idx+i]=c_bottom->v[idx+i]; + } +} +__device__ void Chunk::chunkClmul(Chunk (*a), Element (*e), idx_t idx, Chunk (*c)) +{ + chunk_cell_t my_ans[2][(ord>>(log_warp_size))]={0}; + for(unsigned int k = 0 ; k < ord ; ++k) + { + if(EXTRACT_BIT(e->c,k)) + for(unsigned int t = 0 ; t < (ord>>log_warp_size); ++t) + { + int b = (k>(idx+warp_size*t)); + my_ans[b][t]^=a->v[idx+warp_size*t+(b<>log_warp_size); ++i) + { + c->v[idx+i*warp_size] = my_ans[0][i]; + c->v[ord+idx+i*warp_size] = my_ans[1][i]; + } +} +__device__ void Chunk::aux_k_clmul(Chunk *a, Element* e, len_t len,Chunk* c_shared) +{ + + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + const idx_t chunk_idx = (idx >> Chunk::log_threads_in_chunk); + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); + Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); + for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size) + my_shared_chunk->v[in_chunk_idx+i]=a[chunk_idx].v[in_chunk_idx+i]; + Chunk::chunkClmul(my_shared_chunk,e,in_chunk_idx,my_shared_chunk); + Chunk::chunk_reduce(a+chunk_idx,my_shared_chunk,in_chunk_idx); +} +__global__ void k_clmul(Chunk *a,Element *e,len_t len ) +{ + const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; + __shared__ Chunk c_shared[shared_len<<1]; + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + if(idx>=(len<>>(d_a,d_e,len); +#ifdef __MEASURE + cudaEventRecord(stop,0); +#endif + + //Copy results to host + cudaMemcpy(h_res,d_a,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); + //Free allocated memory. + cudaFree(d_a); + cudaFree(d_e); +#ifdef __MEASURE + cudaEventElapsedTime(&time,start,stop); + printf("Time for the mul: %f ms on %d chunks \n",time,len); +#endif +} + +__global__ void k_add(Chunk (*a), Chunk (*b), len_t l) +{ + unsigned int idx = threadIdx.x+blockIdx.x*blockDim.x; + if(idx>=l*Chunk::cells_in_chunk) + return; + ((chunk_cell_t*)a)[idx]^=((chunk_cell_t*)b)[idx]; +} +__host__ void Chunk::add(Chunk (*h_a),Chunk (*h_b),len_t len) +{ + + //Declare device variables + Chunk (*d_a); + Chunk (*d_b); + + //Define Block and Grid Size. + dim3 blockSize(max_block_size,1,1); + dim3 gridSize(sizeCiel(max_block_size,len),1,1); + + //Allocate Memory on GPU. (global) + cudaMalloc(&d_a,sizeof(Chunk)*len); + cudaMalloc(&d_b,sizeof(Chunk)*len); + + //Copy memory to GPU. + cudaMemcpy(d_a,h_a,sizeof(Chunk)*len,cudaMemcpyHostToDevice); + cudaMemcpy(d_b,h_b,sizeof(Chunk)*len,cudaMemcpyHostToDevice); + + //Launch Kernel + k_add<<>>(d_a,d_b,len); + + //Copy results to CPU memory + cudaMemcpy(h_a,d_a,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); + + //Free allocated memory. + cudaFree(d_a); + cudaFree(d_b); +} +__host__ void Chunk::print() const { + for(unsigned int i = 0 ; i < cells_in_chunk ; ++i){ + cout << bitset(this->v[i])<e[i]); + cout<= k); + my_ans[0][0] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[0][1] ^= ((1-b)*__shfl_down(a_reg[0],warp_size-k))& e_reg; + my_ans[0][1] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + + e_reg=e.v[k+warp_size];; + my_ans[0][1] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[0],32-k))& e_reg; + my_ans[1][0] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][1] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + } + /* + * Reduce + */ +#pragma unroll 4 + for(unsigned int i = 0 ; i < 4 ; ++i){ + b=(in_chunk_idx> shift); + a_reg[1] ^= (my_ans[0][1] >> shift); + + c.v[in_chunk_idx] = a_reg[0]; + c.v[in_chunk_idx+warp_size] = a_reg[1]; +} +__device__ void Chunk::clmul_by_chunk_bShuffle_xor_mask(const Chunk& a,const Chunk& e, const idx_t in_chunk_idx, Chunk& c, const int shift, const idx_t mask_idx){ + /* + * Carryles multiplication + */ + chunk_cell_t a_reg[2]; + chunk_cell_t e_reg; + a_reg[0] = a.v[in_chunk_idx]; + a_reg[1] = a.v[in_chunk_idx+warp_size]; + + chunk_cell_t my_ans[2][2]={0}; + int b; + for(unsigned k = 0 ; k < warp_size; ++k){ + e_reg=e.v[k]; + b= (in_chunk_idx>= k); + my_ans[0][0] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[0][1] ^= ((1-b)*__shfl_down(a_reg[0],warp_size-k))& e_reg; + my_ans[0][1] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + + e_reg=e.v[k+warp_size];; + my_ans[0][1] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[0],32-k))& e_reg; + my_ans[1][0] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][1] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + } + /* + * Reduce + */ +#pragma unroll 4 + for(unsigned int i = 0 ; i < 4 ; ++i){ + b=(in_chunk_idx> shift); + a_reg[1] ^= (my_ans[0][1] >> shift); + + a_reg[0]^=(a_reg[0]&d_alter_mask[mask_idx+1])<<(1<= k); + my_ans[0][0] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[0][1] ^= ((1-b)*__shfl_down(a_reg[0],warp_size-k))& e_reg; + my_ans[0][1] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + + e_reg=e.v[k+warp_size];; + my_ans[0][1] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[0],32-k))& e_reg; + my_ans[1][0] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][1] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + } + /* + * Reduce + */ +#pragma unroll 4 + for(unsigned int i = 0 ; i < 4 ; ++i){ + b=(in_chunk_idx> shift); + c.v[in_chunk_idx+warp_size] ^= (my_ans[0][1] >> shift); +} +__device__ void Chunk::clmul_by_chunk_bShuffle(const Chunk& a,const Chunk& e, const idx_t in_chunk_idx, Chunk& c){ + /* + * Carryles multiplication + */ + chunk_cell_t a_reg[2]; + chunk_cell_t e_reg; + a_reg[0] = a.v[in_chunk_idx]; + a_reg[1] = a.v[in_chunk_idx+warp_size]; + + chunk_cell_t my_ans[2][2]={0}; + int b; + for(unsigned k = 0 ; k < warp_size; ++k){ + e_reg=e.v[k]; + b= (in_chunk_idx>= k); + my_ans[0][0] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[0][1] ^= ((1-b)*__shfl_down(a_reg[0],warp_size-k))& e_reg; + my_ans[0][1] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + + e_reg=e.v[k+warp_size]; + my_ans[0][1] ^= (b*__shfl_up(a_reg[0],k)) & e_reg; + my_ans[1][0] ^= ((1-b)*__shfl_down(a_reg[0],32-k))& e_reg; + my_ans[1][0] ^= (b*__shfl_up(a_reg[1],k)) & e_reg; + my_ans[1][1] ^= ((1-b)*__shfl_down(a_reg[1],32-k))& e_reg; + } + /* + * Reduce + */ +#pragma unroll 4 + for(unsigned int i = 0 ; i < 4 ; ++i){ + b=(in_chunk_idx>(log_warp_size))]={0}; + int b; +// const int l = ord>>log_warp_size; + for(unsigned int k = 0 ; k < ord ; ++k) +#pragma unroll 2 + for(unsigned int t = 0 ; t < 2; ++t) + { + b = (k>(idx+warp_size*t)); + my_ans[b][t]^=a.v[idx+warp_size*t+(b<v[idx+i*warp_size] = my_ans[0][i]; + c->v[ord+idx+i*warp_size] = my_ans[1][i]; + } +} +__global__ void k_mul_chunk(Chunk* cs, Chunk* c, len_t cs_len) +{ + const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; + __shared__ Chunk c_shared[shared_len<<1]; + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + if(idx>=(cs_len<> Chunk::log_threads_in_chunk); + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); + Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); + for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ + my_shared_chunk->v[in_chunk_idx+i]=cs[chunk_idx].v[in_chunk_idx+i]; + my_shared_chunk[1].v[in_chunk_idx+i]=c->v[in_chunk_idx+i]; + } + Chunk::clmul_by_chunk(my_shared_chunk[0],my_shared_chunk[1],in_chunk_idx,my_shared_chunk); + Chunk::chunk_reduce(cs+chunk_idx,my_shared_chunk,in_chunk_idx); +} +__global__ void k_mul_chunk_xor(Chunk* cs, Chunk* c, len_t cs_len,Chunk* to_xor, int shift = 0) +{ + const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; + __shared__ Chunk c_shared[shared_len<<1]; + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + if(idx>=(cs_len<> Chunk::log_threads_in_chunk); + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); + Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); + for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ + my_shared_chunk->v[in_chunk_idx+i]=cs[chunk_idx].v[in_chunk_idx+i]; + my_shared_chunk[1].v[in_chunk_idx+i]=c->v[in_chunk_idx+i]; + } + Chunk::clmul_by_chunk(my_shared_chunk[0],my_shared_chunk[1],in_chunk_idx,my_shared_chunk); + Chunk::chunk_reduce_xor(cs+chunk_idx,my_shared_chunk,in_chunk_idx,to_xor,shift); +} +//Mul a chunk by a chunk +void Chunk::chunk_mul(Chunk (* h_a), Chunk (*h_b) , len_t len, Chunk (*h_res), bool copy, bool do_xor, int shift){ +#ifdef __MEASURE + cudaEvent_t start,stop; + float time; + cudaEventCreate(&start); + cudaEventCreate(&stop); +#endif + //Declare device variables + Chunk (*d_a); + Chunk (*d_b); + + //Define Block and Grid Size. + dim3 blockSize(max_block_size,1,1); + dim3 gridSize(sizeCiel(len<>>(d_a,d_b,len,d_a,shift); + else + k_mul_chunk<<>>(d_a,d_b,len); +#ifdef __MEASURE + cudaEventRecord(stop,0); +#endif + + if(copy){ + //Copy results to host + cudaMemcpy(h_res,d_a,sizeof(Chunk)*len,cudaMemcpyDeviceToHost); + //Free allocated memory. + cudaFree(d_a); + cudaFree(d_b); + } +#ifdef __MEASURE + cudaEventElapsedTime(&time,start,stop); + printf("Time for the mul: %f ms on %d chunks \n",time,len); +#endif +} + + + + + + + + + +/*-------------------------------------*/ + +/*** GPU FFT ***/ +const unsigned int multThreadsInBlock = 1024; + +__global__ void k_multiExp_mult_bShuffle(Chunk* d_a, Chunk* d_b , len_t b_len , len_t a_len){ + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + if(idx>=(a_len<> Chunk::log_threads_in_chunk); + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); +// const idx_t shared_chunk_idx = ; + Chunk::clmul_by_chunk_bShuffle(d_a[chunk_idx],d_b[chunk_idx & (b_len-1)],in_chunk_idx,d_a[chunk_idx]); +// Chunk::chunk_reduce(d_a+chunk_idx,my_shared_chunk,in_chunk_idx); +} +__global__ void k_multiExp_mult(Chunk* d_a, Chunk* d_b , len_t b_len , len_t a_len){ +// const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; +// __shared__ Chunk c_shared[shared_len<<1]; + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + if(idx>=(a_len<> Chunk::log_threads_in_chunk); + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); +// const idx_t shared_chunk_idx = ; +// Chunk* my_shared_chunk = c_shared+(((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk))<<1); +// for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ +// my_shared_chunk->v[in_chunk_idx+i]=d_a[chunk_idx].v[in_chunk_idx+i]; +// my_shared_chunk[1].v[in_chunk_idx+i]=d_b[chunk_idx & (b_len - 1)].v[in_chunk_idx+i]; +// } + Chunk::clmul_by_chunk_bShuffle(d_a[chunk_idx],d_b[chunk_idx & (b_len - 1)],in_chunk_idx,d_a[chunk_idx]); +// Chunk::chunk_reduce(d_a+chunk_idx,my_shared_chunk,in_chunk_idx); +} + +/* + * Gets as input: + * d_a - the polynomial. + * d_b - the multiexponent of current b_m for on strip of elements. + * b_len - length of d_b in chunks. + * + * Multiplies chunk d_a[i] by chunk d_b[i mod b_len]. + */ +void GPU_FFT::multiExp_mult(len_t a_len, Chunk* d_a , Chunk* d_b , len_t b_len){ + unsigned int threads = (a_len<>>(d_a,d_b,b_len, threads>>Chunk::log_elements_in_chunk); +} + +void GPU_FFT::multiExponentiate_gpu(const FFT* fft,Chunk* d_chunk_P, len_t p_len, len_t dim, Chunk* d_exp ){ + //Copy exps to memory. + if(dim<=Chunk::log_elements_in_chunk){ + cudaMemcpy(d_exp,fft->gpu_exp[fft->basis.getSize()-dim], + sizeof(Chunk),cudaMemcpyHostToDevice); + multiExp_mult(p_len,d_chunk_P,d_exp,1); + } else { + cudaMemcpy(d_exp,fft->gpu_exp[fft->basis.getSize()-dim], + sizeof(Chunk)*(1<<(dim-Chunk::log_elements_in_chunk)),cudaMemcpyHostToDevice); + multiExp_mult(p_len,d_chunk_P,d_exp,1<<(dim-Chunk::log_elements_in_chunk)); + } + +} + +void GPU_FFT::imultiExponentiate_gpu(const FFT* fft,Chunk* d_chunk_P, len_t p_len, len_t dim, Chunk* d_exp ){ + //Copy exps to memory. + if(dim<=Chunk::log_elements_in_chunk){ + cudaMemcpy(d_exp,fft->gpu_i_exp[fft->basis.getSize()-dim], + sizeof(Chunk),cudaMemcpyHostToDevice); + multiExp_mult(p_len,d_chunk_P,d_exp,1); + } else { + cudaMemcpy(d_exp,fft->gpu_i_exp[fft->basis.getSize()-dim], + sizeof(Chunk)*(1<<(dim-Chunk::log_elements_in_chunk)),cudaMemcpyHostToDevice); + multiExp_mult(p_len,d_chunk_P,d_exp,1<<(dim-Chunk::log_elements_in_chunk)); + } + +} + +__device__ void taylor_smaller_than_chunk(Chunk* chunk,idx_t in_chunk_idx, len_t t_dim, len_t p_len, idx_t idx){ + if(idx >= ((p_len) << (Chunk::log_cells_in_chunk))) + return; + //Performs the rest of the expansion. + chunk_cell_t cell = chunk->v[in_chunk_idx]; + for(; t_dim >=2 ; --t_dim){ + cell ^= (cell & (d_chunk_cell_mask[t_dim]<<((1<>(1<<(t_dim-2)); + cell ^= (cell & (d_chunk_cell_mask[t_dim]<<(1<<(t_dim-1))))>>(1<<(t_dim-2)); + } + chunk->v[in_chunk_idx]= cell; +} +__global__ void k_taylorExpansion_iteration_large(Chunk* d_chunk_P , len_t p_len , len_t t_dim){ + idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; + idx_t in_chunk_idx = idx & (Chunk::cells_in_chunk-1); + /* + * flag = true if current sub-polynomial fits in a thread block. + */ + bool flag = (t_dim-Chunk::log_elements_in_chunk <= log_max_block_size-Chunk::log_cells_in_chunk + 2); + if(idx >= ((p_len) << (Chunk::log_cells_in_chunk-2))) + return; + do { + len_t sub_len = (1<<(t_dim-Chunk::log_elements_in_chunk)); + idx_t chunk_idx = idx >> (Chunk::log_cells_in_chunk); + chunk_idx = (chunk_idx /(sub_len>>2))*(sub_len) + ((chunk_idx) & ((sub_len>>2)-1)); + d_chunk_P[(sub_len>>1) + chunk_idx ].v[in_chunk_idx] ^= d_chunk_P[(3*(sub_len>>2)) + chunk_idx].v[in_chunk_idx]; + d_chunk_P[(sub_len>>2) + chunk_idx ].v[in_chunk_idx] ^= d_chunk_P[(sub_len>>1) + chunk_idx].v[in_chunk_idx]; + if(flag) + __syncthreads(); + --t_dim; + } while ( flag && t_dim > Chunk::log_elements_in_chunk + 1); + //If number of threads needed is less than a thread block - we can just continue! +} +__global__ void k_taylorExpansion_iteration_twoChunks(Chunk* d_chunk_P, len_t p_len, len_t t_dim){ + idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; + idx_t in_chunk_idx = idx & (Chunk::cells_in_chunk-1); + if(idx >= ((p_len) << (Chunk::log_cells_in_chunk-1))) + return; + idx_t chunk_idx= (idx >> (Chunk::log_cells_in_chunk) ) << 1; + d_chunk_P[(chunk_idx)+1].v[in_chunk_idx]^= (d_chunk_P[(chunk_idx)+1].v[in_chunk_idx]>>(Chunk::elements_in_chunk>>1)); + d_chunk_P[(chunk_idx)].v[in_chunk_idx]^= (d_chunk_P[(chunk_idx)+1].v[in_chunk_idx]<<(Chunk::elements_in_chunk>>1)); +} +__global__ void k_taylorExpansion_iteration_singleChunk(Chunk* d_chunk_P, len_t p_len, len_t t_dim){ + + idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; + idx_t in_chunk_idx = idx & (Chunk::cells_in_chunk-1); + //Performs the rest of the expansion. + idx_t chunk_idx = (idx >> (Chunk::log_cells_in_chunk)); + taylor_smaller_than_chunk(d_chunk_P+chunk_idx,in_chunk_idx,t_dim,p_len,idx); +} +void taylorExpansion_iteration(const FFT* fft, Chunk * d_chunk_P , len_t p_len , len_t t_dim){ + unsigned int threads; + dim3 blockSize(max_block_size,1,1); + if( t_dim >= Chunk::log_elements_in_chunk + 2){ + threads = p_len<<(Chunk::log_cells_in_chunk-2); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_taylorExpansion_iteration_large<<>>(d_chunk_P,p_len,t_dim); + } else if (t_dim == Chunk::log_elements_in_chunk + 1){ + threads = p_len <<(Chunk::log_cells_in_chunk-1); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_taylorExpansion_iteration_twoChunks<<>>(d_chunk_P,p_len,t_dim); + } else { + threads = p_len << Chunk::log_cells_in_chunk; + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_taylorExpansion_iteration_singleChunk<<>>(d_chunk_P,p_len,t_dim); + } +// k_taylorExpansion_iteration<<>>(d_chunk_P,p_len,t_dim); +} +void GPU_FFT::taylorExpansion_gpu(const FFT* fft, Chunk* d_chunk_P, len_t p_len , len_t dim){ + len_t t_dim = dim; + /* + * The sub-polynomial requires more than a single thread block. + */ + while(t_dim+Chunk::log_cells_in_chunk > Chunk::log_elements_in_chunk+log_max_block_size + 2){ + taylorExpansion_iteration(fft,d_chunk_P,p_len,t_dim); + --t_dim; + } + /* + * The sub-polynomial requires more than 2 chunks BUT less than a single thread block. + */ + if(t_dim >= Chunk::log_elements_in_chunk + 2){ + taylorExpansion_iteration(fft,d_chunk_P,p_len,t_dim); + t_dim = Chunk::log_elements_in_chunk+1; + } + /* + * Each sub-polynomial takes exactly two chunks. + */ + if(t_dim == Chunk::log_elements_in_chunk + 1){ + taylorExpansion_iteration(fft,d_chunk_P,p_len,t_dim); + --t_dim; + } + /* + * Each sub-polynomial takes at most one chunk. + */ + taylorExpansion_iteration(fft,d_chunk_P,p_len,t_dim); +} + + /* + * Input: + * 1) d_chunk_p - The polynomial on device's memory. + * 2) p_len - number of chunks in d_cunnk_p. + * 3) chunk_idx - the chunk current thread has to deal with. + * 4) in_chunk_idx - the number of the cell the current thread deals with. + * 5) t_dim - The dim of the original (input) subpolynomial. + * + * This function performs what a single thread does when performing the partition function, on a single chunk. + */ +__device__ void partition_in_chunk(Chunk* d_chunk_p, len_t p_len , idx_t in_chunk_idx, len_t t_dim){ + chunk_cell_t ans = d_chunk_p->v[in_chunk_idx]; + for(unsigned int i = 2 ; i <= Chunk::log_elements_in_chunk && i<=t_dim ; ++i){ + ans = (ans & d_chunk_cell_mask[i]) | + (ans & (d_chunk_cell_mask[i]<<((1<<(i))-(1<<(i-2))))) | + (ans & (d_chunk_cell_mask[i]<<(1<<(i-1))))>>(1<<(i-2)) | + (ans & (d_chunk_cell_mask[i]<<(1<<(i-2))))<<(1<<(i-2)); + } + d_chunk_p->v[in_chunk_idx]=ans; +} + +__device__ void partition_two_chunks(Chunk* d_chunk_p, idx_t in_chunk_idx){ + chunk_cell_t ans[2]; + chunk_cell_t load[2]; + const chunk_cell_t mask = d_chunk_cell_mask[Chunk::log_elements_in_chunk+1]; + load[0]=d_chunk_p->v[in_chunk_idx]; + load[1]=d_chunk_p[1].v[in_chunk_idx]; + ans[0]=(load[0] & mask) | ((load[1] & mask)<<(Chunk::elements_in_chunk>>1)); + ans[1]=(load[1] & (mask<< (Chunk::elements_in_chunk>>1)) )| + ((load[0] >> (Chunk::elements_in_chunk>>1)) & mask); + d_chunk_p->v[in_chunk_idx] = ans[0]; + d_chunk_p[1].v[in_chunk_idx] = ans[1]; +} +__device__ void partition_general(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, idx_t chunk_idx, idx_t in_chunk_idx, idx_t t_dim){ + if(chunk_idx & 1) + d_chunk_p_dst[(chunk_idx>>1) + (1<<(t_dim-1))].v[in_chunk_idx] = d_chunk_p_src[chunk_idx].v[in_chunk_idx]; + else + d_chunk_p_dst[chunk_idx>>1].v[in_chunk_idx] = d_chunk_p_src[chunk_idx].v[in_chunk_idx]; +} + +__global__ void k_partition_iteration_two_chunks(Chunk* d_chunk_p_src, len_t p_len){ + idx_t idx = threadIdx.x + blockIdx.x * blockDim.x; + idx_t chunk_idx = (idx >> Chunk::log_cells_in_chunk)<<1; + idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); + if(chunk_idx >= p_len){ + return; + } + partition_two_chunks(d_chunk_p_src+chunk_idx, in_chunk_idx); +} +__global__ void k_partition_iteration_in_chunk(Chunk* d_chunk_p_src, len_t p_len, len_t t_dim){ + idx_t idx = threadIdx.x + blockIdx.x * blockDim.x; + idx_t chunk_idx = idx >> Chunk::log_cells_in_chunk; + idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); + if(chunk_idx >= p_len){ + return; + } + partition_in_chunk(d_chunk_p_src+chunk_idx,p_len,in_chunk_idx,t_dim); +} +__global__ void k_partition_iteration_general(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, len_t p_len, len_t t_dim){ + idx_t idx = threadIdx.x + blockIdx.x * blockDim.x; + idx_t chunk_idx = idx >> Chunk::log_cells_in_chunk; + idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); + idx_t base = chunk_idx ^ (chunk_idx & andMask(t_dim-Chunk::log_elements_in_chunk)); + chunk_idx &= andMask(t_dim-Chunk::log_elements_in_chunk); + if(base+chunk_idx >= p_len) + return; + partition_general(d_chunk_p_src+base,d_chunk_p_dst+base, chunk_idx, in_chunk_idx,t_dim-Chunk::log_elements_in_chunk ); +} +/* + * If t_dim > Chunk::log_elements_in_chunk+1 the result is written in dst, otherwise it will be written in src. + */ +bool GPU_FFT::partition(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, len_t p_len, len_t t_dim){ + len_t threads = p_len << Chunk::log_cells_in_chunk; + dim3 blockSize(max_block_size,1,1); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_partition_iteration_in_chunk<<>>(d_chunk_p_src,p_len,t_dim); + if(t_dim >= Chunk::log_elements_in_chunk +1){ + dim3 gridSizeTwoChunks(sizeCiel(threads>>1,max_block_size),1,1); + k_partition_iteration_two_chunks<<>>(d_chunk_p_src,p_len); + } + if(t_dim > Chunk::log_elements_in_chunk+1){ + k_partition_iteration_general<<>>(d_chunk_p_src,d_chunk_p_dst,p_len,t_dim); + return true; + } + return false; +} + +//__global__ void k_xor_chunk_vector_by_single_chuk(Chunk* chunk_vec, Chunk* single_chunk, len_t chunk_vec_len){ +// idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; +// idx_t chunk_idx = idx >> Chunk::log_cells_in_chunk; +// idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); +// if(chunk_idx >= chunk_vec_len){ +// return; +// } +// chunk_vec[chunk_idx].v[in_chunk_idx] ^= single_chunk->v[in_chunk_idx]; +// return; +//} +__global__ void k_copy_and_shift_vec(Chunk* d_chunk_src, Chunk* d_chunk_dst, len_t p_len){ + + const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; + __shared__ Chunk c_shared[shared_len<<1]; + + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + const idx_t chunk_idx = (idx >> Chunk::log_threads_in_chunk); + + if(chunk_idx >= p_len){ + return; + } + + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); + Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); + + for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ + my_shared_chunk->v[in_chunk_idx+i]=d_chunk_src[chunk_idx].v[in_chunk_idx+i]; + my_shared_chunk[1].v[in_chunk_idx+i]=d_linear_mul->v[in_chunk_idx+i]; + } + + chunk_cell_t tmp; + for(unsigned int i = 0 ; i < Element::ord ; i+=warp_size){ + tmp = my_shared_chunk->v[in_chunk_idx+i]; + my_shared_chunk->v[in_chunk_idx+i] = (tmp & 0xaaaaaaaa) ^ ((tmp & 0xaaaaaaaa)>>1); + d_chunk_dst[chunk_idx].v[in_chunk_idx+i] = (tmp & 0x55555555) ^ ((tmp & 0x55555555)<<1); + } + Chunk::clmul_by_chunk(my_shared_chunk[0],my_shared_chunk[1],in_chunk_idx,my_shared_chunk); + Chunk::chunk_reduce(d_chunk_src+chunk_idx,my_shared_chunk,in_chunk_idx); + for(unsigned int i = 0 ; i < Element::ord ; i+=warp_size){ + d_chunk_src[chunk_idx].v[in_chunk_idx+i] ^= d_chunk_dst[chunk_idx].v[in_chunk_idx+i]; + } + return; +} +//__global__ void k_xor_chunk_vectors(Chunk* d_chunk, Chunk* x, len_t p_len){ +// idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; +// idx_t chunk_idx = idx >> Chunk::log_cells_in_chunk; +// idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); +// if(chunk_idx >= p_len){ +// return; +// } +// d_chunk[chunk_idx].v[in_chunk_idx] ^= x[chunk_idx].v[in_chunk_idx]; +//} +void GPU_FFT::linearEvaluation(Chunk* d_chunk_p,Chunk* d_chunk_p_cpy, len_t p_len){ + len_t threads = p_len << Chunk::log_threads_in_chunk; + dim3 blockSize(max_block_size,1,1); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_copy_and_shift_vec<<>>(d_chunk_p,d_chunk_p_cpy,p_len); +} +__global__ void k_subspaceAdd_general(Chunk* d_a, len_t a_len, len_t b_len){ + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + idx_t chunk_idx = (idx >> Chunk::log_cells_in_chunk); + chunk_idx = ((chunk_idx / b_len)*(2*b_len)) +b_len+(chunk_idx & (b_len-1)); + if(chunk_idx >= a_len){ + return; + } + const idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); + d_a[chunk_idx].v[in_chunk_idx]^=d_a[chunk_idx-b_len].v[in_chunk_idx]; +} +__global__ void k_subspaceMult_general(Chunk* d_a, Chunk* d_b , len_t b_len , len_t a_len){ + const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; + __shared__ Chunk c_shared[shared_len<<1]; + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + idx_t chunk_idx = (((idx >> Chunk::log_threads_in_chunk))/b_len)*(2*b_len)+b_len; + const idx_t b_chunk_idx = (idx>>Chunk::log_threads_in_chunk) & (b_len - 1); + chunk_idx+=b_chunk_idx; + if(chunk_idx >= a_len){ + return; + } + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); + Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); + for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ + my_shared_chunk->v[in_chunk_idx+i]=d_a[chunk_idx].v[in_chunk_idx+i]; + my_shared_chunk[1].v[in_chunk_idx+i]=d_b[b_chunk_idx].v[in_chunk_idx+i]; + } + Chunk::clmul_by_chunk_bShuffle_xor(d_a[chunk_idx],d_b[b_chunk_idx],in_chunk_idx,d_a[chunk_idx-b_len],0); +// Chunk::clmul_by_chunk(my_shared_chunk[0],my_shared_chunk[1],in_chunk_idx,my_shared_chunk); +// Chunk::chunk_reduce_xor(d_a+chunk_idx,my_shared_chunk,in_chunk_idx,d_a+chunk_idx-b_len,0); +} +__global__ void k_subspaceMult_chunk_and_add(Chunk* d_a, Chunk* d_b , len_t log_elements_in_b ,len_t a_len ){ + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + const idx_t chunk_idx = (idx >> Chunk::log_threads_in_chunk); + if(chunk_idx >= a_len){ + return; + } + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + Chunk::clmul_by_chunk_bShuffle_xor_mask(d_a[chunk_idx],*d_b,in_chunk_idx,d_a[chunk_idx],1<=Chunk::log_elements_in_chunk){ + len_t threadsMul = p_len << (Chunk::log_threads_in_chunk-1); + len_t threadsAdd = p_len << (Chunk::log_cells_in_chunk-1); + dim3 block(multThreadsInBlock,1,1); + dim3 gridMul(sizeCiel(threadsMul,multThreadsInBlock),1,1); + dim3 gridAdd(sizeCiel(threadsAdd,multThreadsInBlock),1,1); + k_subspaceMult_general<<>>(d_chunk_p, subspace, (1<<(dim-Chunk::log_elements_in_chunk)),p_len); + k_subspaceAdd_general<<>>(d_chunk_p,p_len,1<<(dim-Chunk::log_elements_in_chunk)); + } else { + len_t threadsMul = p_len << Chunk::log_threads_in_chunk; + dim3 block(multThreadsInBlock,1,1); + dim3 gridMul(sizeCiel(threadsMul,multThreadsInBlock),1,1); + k_subspaceMult_chunk_and_add<<>>(d_chunk_p,subspace, dim , p_len); + } +} +void GPU_FFT::setUpConstantMemory(const FFT* fft){ + /* + * Masks for taylor expansion on small dimensions. + */ + cudaMemcpyToSymbol(d_chunk_cell_mask,taylorExp_masks,sizeof(chunk_cell_t)*(Chunk::log_elements_in_chunk+2)); + /* + * Masks for WFromUV + */ + cudaMemcpyToSymbol(d_alter_mask,alter_masks,sizeof(chunk_cell_t)*(Chunk::log_elements_in_chunk+1)); + /* + * Table for partition operation on small dimension. + */ + cudaMemcpyToSymbol(d_partition,partition_byte,sizeof(chunk_cell_t)*(1<linear_mul,sizeof(Chunk)); + cudaMemcpyToSymbol(d_ilinear_mul,&fft->ilinear_mul,sizeof(Chunk)); +} +/* + * p is a pointer to a chunk array on gpu, l is its length. + * prints that array. + */ +void printChunkOnGPU(Chunk* p , len_t l){ +// TODO: No real need for printing and this break the building of FFT as separate library +/* + Chunk* h_p = Tests::copyFromGPU(p,l); + for(unsigned int i = 0 ; i < l ; ++i){ + h_p[i].print(); + std::cout << std::endl << std::endl; + + } + free(h_p); + std::cout << std::endl << std::endl; +*/ +} + +/* + * This is the gpu implementation of the FFT when it fits into a single thread block. + * In that case, the whole FFT can be calculated using a single kernel invocation and some additional + * synchronization primitives. + * + * In this implementation, each WARP is responsible for a single chunk, therefore if 32 threads compose a single warp and + * we use 1024 threads in a thread block, then each thread block is responsible for 32 chunks. + * + * 0) First we load ALL subspaces and exponents into global memory in a special pre-allocated array. + * We also allocate 64 Chunks long array in shared memory. + * + * 1) We load the shared memory with the relevant polynomial (depends on the index). + * 2) Series of Multiexp -> taylorExp -> partition, all inside local memory. + * 2.1) Before Each Multiexp we load 32 chunks of exps to shared memory. + * 3) Linear evaluation. + * 4) Series of WFromUV, while loading subspaces from global memory. + * + * This whole implementation assumes that: + * Chunk::log_cells_in_chunk = 1+ Chunk::log_elements_in_chunk + */ + +__device__ void InTB_preMulExpand( Chunk* const d_a, + const Chunk* const d_b, + const len_t b_len , + const idx_t idx , + const idx_t chunkIdx , + const idx_t in_chunk_idx){ + chunk_cell_t load[2]; + load[0]=d_a[chunkIdx].v[in_chunk_idx]; + load[1]=d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]; + __syncthreads(); + d_a[chunkIdx<<1].v[in_chunk_idx] = load[0]; + d_a[chunkIdx<<1].v[in_chunk_idx + Chunk::elements_in_chunk] = load[1]; + d_a[(chunkIdx<<1)+1].v[in_chunk_idx] + = d_b[chunkIdx & (b_len-1)].v[in_chunk_idx]; + d_a[(chunkIdx<<1)+1].v[in_chunk_idx + Chunk::elements_in_chunk] + = d_b[chunkIdx & (b_len-1)].v[in_chunk_idx + Chunk::elements_in_chunk]; + __syncthreads(); +} +/* + * Takes every second chunk and puts all of them in the first half. + * Second half output is unknown. + * 1) d_a - Pointer for shmem array of the polynomial. + * 2) idx - index of the thread. + * 3) chunkIdx - (idx / Chunk::elemetns_in_chunk). + * 4) in_chunk_idx - (idx % Chunk::elements_in_chunk). + */ +__device__ void InTB_postMulShrink( Chunk * const d_a, + const idx_t idx , + const idx_t chunkIdx , + const idx_t in_chunk_idx){ + chunk_cell_t load[2]; + load[0]=d_a[chunkIdx<<1].v[in_chunk_idx]; + load[1]=d_a[chunkIdx<<1].v[in_chunk_idx+Chunk::elements_in_chunk]; + __syncthreads(); + d_a[chunkIdx].v[in_chunk_idx]=load[0]; + d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]=load[1]; + __syncthreads(); +} +/* + * Multiplies d_a[0] by d_a[1] and writes the answer at d_a[0]. + * The state of d_a[1] is unknown at the end. + * + * 1) d_a - the chunk the mult. + * 2) in_chunk_idx - Is thread index % Chunk::elemets_in_chunk + */ +__device__ void InTB_Mult(Chunk* const d_a, const idx_t in_chunk_idx){ + Chunk::clmul_by_chunk(*d_a,d_a[1],in_chunk_idx,d_a); + Chunk::chunk_reduce(d_a,d_a,in_chunk_idx); + __syncthreads(); +} +/* + * This is the multi exponentiation. + * 1) d_a - pointer to the shmem. + * 2) exp - pointer to the exponents to multiply (global mem). + * 3) dim - dimension of multiexponentiation. + * 4) idx - thread index. + * 5) chunkIdx - idx/Chunk::elemets_in_chunk. + * 6) in_chunk_idx - idx%Chunk::elements_in_chunk. + */ + __device__ void InTB_MultiExp( Chunk* const d_a, + const Chunk* const exp, + const len_t exp_len, + const idx_t idx, + const idx_t chunkIdx, + const idx_t in_chunk_idx, + const len_t half_d_a_len){ + chunk_cell_t load[2]; + chunk_cell_t swap; + load[0] = d_a[half_d_a_len + chunkIdx].v[in_chunk_idx]; + load[1] = d_a[half_d_a_len + chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]; + + InTB_preMulExpand(d_a,exp,exp_len,idx,chunkIdx,in_chunk_idx); + InTB_Mult(d_a+(chunkIdx<<1),in_chunk_idx); + InTB_postMulShrink(d_a,idx,chunkIdx,in_chunk_idx); + + swap = load[0]; + load[0]=d_a[chunkIdx].v[in_chunk_idx]; + d_a[chunkIdx].v[in_chunk_idx] = swap; + swap = load[1]; + load[1]=d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]; + d_a[chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk] = swap; + + InTB_preMulExpand(d_a,exp+((half_d_a_len) & (exp_len>>1)),exp_len,idx,chunkIdx,in_chunk_idx); + InTB_Mult(d_a+(chunkIdx<<1),in_chunk_idx); + InTB_postMulShrink(d_a,idx,chunkIdx,in_chunk_idx); + + d_a[half_d_a_len + chunkIdx].v[in_chunk_idx] = d_a[chunkIdx].v[in_chunk_idx]; + d_a[half_d_a_len + chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk] = + d_a[chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk]; + d_a[chunkIdx].v[in_chunk_idx] = load[0]; + d_a[chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk] = load[1]; + __syncthreads(); + + } +/* + * Taylor Expansion + * + * 1) d_a is the pointer to the beginning of the polynomial in shmem. + * 2) dim>=2. Also dim<=10. + * 3) idx - the idx of the thread. + * 4) in_chunk_idx - (idx % (Chunk::chunk_cells_in_chunk)). + * 5) chunkIdx - index of chunk INSIDE sub-polynomial. + */ +__device__ void InTB_TaylorExp( Chunk* const d_a, + idx_t dim, + const idx_t idx, + const idx_t in_chunk_idx, + const idx_t chunkIdx, + const idx_t half_d_a_len){ + len_t l,s; + /* + * If more than a single chunk is needed for a subpolynomial + */ + if(dim> Chunk::log_elements_in_chunk){ + for(; dim > Chunk::log_elements_in_chunk+1 ; --dim){ + l=dim-Chunk::log_elements_in_chunk; + s=1<<(l-2); + l=((chunkIdx>>(l-2))<>1; + l=chunkIdx*2; + for(unsigned int i = 0 ; i < 2 ; ++i){ + d_a[l+1+i*half_d_a_len].v[in_chunk_idx]^= + (d_a[l+1+i*half_d_a_len].v[in_chunk_idx]>>(Chunk::elements_in_chunk>>1)); + d_a[l+i*half_d_a_len].v[in_chunk_idx]^= + (d_a[l+1+i*half_d_a_len].v[in_chunk_idx]<<(Chunk::elements_in_chunk>>1)); + } + --dim; + __syncthreads(); + } + unsigned int i = dim; + /* + * Code duplicated to prevent additional register usage for loop counter, assuming that: + * Chunk::log_cells_in_chunk = Chunk::log_elements_in_chunk+1. + */ + for(unsigned int j = 0 ; j < 4 ; ++j ) { + chunk_cell_t cell = d_a[chunkIdx+j*(half_d_a_len>>1)].v[in_chunk_idx]; + for(; dim >=2 ; --dim){ + cell ^= (cell & + (d_chunk_cell_mask[dim]<<((1<>(1<<(dim-2)); + cell ^= (cell & + (d_chunk_cell_mask[dim]<<(1<<(dim-1))))>>(1<<(dim-2)); + } + d_a[chunkIdx+j*(half_d_a_len>>1)].v[in_chunk_idx] = cell ; + dim = i; + } + __syncthreads(); +} +/* + * This is the partition operation + * 1)d_a - the shmem ptr for the polynomial. + * 2) dim - the dimension of partition. + * 3) idx - the thread index. + * 4) chunk_idx - (idx >> Chunk::log_elements_in_chunk). + * 5) in_chunk_idx - (idx % Chunk::elements_in_chunk). + * 6) d_a_len - log length of subpoly in chunks. + */ +__device__ void InTB_partition( Chunk* const d_a, + const len_t dim, + const idx_t idx, + const idx_t chunk_idx, + const idx_t in_chunk_idx, + const len_t d_a_len, + const len_t half_d_a_len){ + /* + * Assumes: + * Chunk::log_cells_in_chunk = 1+ Chunk::log_elements_in_chunk + */ + idx_t i=2; + chunk_cell_t ans[2]; + for(unsigned int j = 0 ; j < 2 ; ++j){ + ans[0]= d_a[chunk_idx + j*half_d_a_len].v[in_chunk_idx]; + ans[1]=d_a[chunk_idx + j*half_d_a_len].v[in_chunk_idx + (Chunk::cells_in_chunk>>1)]; + for(i=2; i <= Chunk::log_elements_in_chunk && i<=dim ; ++i){ + ans[0] = (ans[0] & d_chunk_cell_mask[i]) | + (ans[0] & (d_chunk_cell_mask[i]<<((1<<(i))-(1<<(i-2))))) | + (ans[0] & (d_chunk_cell_mask[i]<<(1<<(i-1))))>>(1<<(i-2)) | + (ans[0] & (d_chunk_cell_mask[i]<<(1<<(i-2))))<<(1<<(i-2)); + ans[1] = (ans[1] & d_chunk_cell_mask[i]) | + (ans[1] & (d_chunk_cell_mask[i]<<((1<<(i))-(1<<(i-2))))) | + (ans[1] & (d_chunk_cell_mask[i]<<(1<<(i-1))))>>(1<<(i-2)) | + (ans[1] & (d_chunk_cell_mask[i]<<(1<<(i-2))))<<(1<<(i-2)); + } + d_a[chunk_idx + j*half_d_a_len].v[in_chunk_idx]=ans[0]; + d_a[chunk_idx + j*half_d_a_len].v[in_chunk_idx+(Chunk::cells_in_chunk>>1)] = ans[1]; + } + __syncthreads(); + + if(dim<=Chunk::log_elements_in_chunk){ + return; + } + idx_t s = (idx& andMask(log_max_block_size))>> Chunk::log_cells_in_chunk; + s<<=1; + idx_t t = idx & andMask(Chunk::log_cells_in_chunk); + + for(unsigned int j = 0 ; j < 2 ; ++j){ + ans[0]=d_a[s + j*half_d_a_len].v[t]; + ans[1]=d_a[s+1 + j*half_d_a_len].v[t]; + d_a[s+j*half_d_a_len].v[t] = (ans[0] & d_chunk_cell_mask[Chunk::log_elements_in_chunk+1]) | ((ans[1] & d_chunk_cell_mask[Chunk::log_elements_in_chunk+1])<<(Chunk::elements_in_chunk>>1)); + d_a[s+1 + j*half_d_a_len].v[t] = (ans[1] & (d_chunk_cell_mask[Chunk::log_elements_in_chunk+1]<< (Chunk::elements_in_chunk>>1)) )| + ((ans[0] >> (Chunk::elements_in_chunk>>1)) & d_chunk_cell_mask[Chunk::log_elements_in_chunk+1]); + } + __syncthreads(); + + if(dim<=Chunk::log_elements_in_chunk+1){ + return; + } + ans[0] = d_a[chunk_idx].v[in_chunk_idx]; + ans[1] = d_a[chunk_idx].v[in_chunk_idx+Chunk::elements_in_chunk]; + chunk_cell_t ans_u[2]; + ans_u[0] = d_a[chunk_idx+half_d_a_len].v[in_chunk_idx]; + ans_u[1] = d_a[chunk_idx+ half_d_a_len].v[in_chunk_idx+Chunk::elements_in_chunk]; + __syncthreads(); + s = (chunk_idx >> d_a_len)<<(d_a_len); + t = chunk_idx & andMask(d_a_len); + if(t & 1){ + d_a[s+(t>>1) + (1<<(d_a_len-1))].v[in_chunk_idx] = ans[0]; + d_a[s+(t>>1) + (1<<(d_a_len-1))].v[in_chunk_idx+Chunk::elements_in_chunk] = ans[1]; + } else { + d_a[s+(t>>1)].v[in_chunk_idx] = ans[0]; + d_a[s+(t>>1)].v[in_chunk_idx+Chunk::elements_in_chunk] = ans[1]; + } + + s=((chunk_idx+half_d_a_len) >> d_a_len)<>1) + (1<<(d_a_len-1))].v[in_chunk_idx] = ans_u[0]; + d_a[s+(t>>1) + (1<<(d_a_len-1))].v[in_chunk_idx+Chunk::elements_in_chunk] = ans_u[1]; + } else { + d_a[s+(t>>1)].v[in_chunk_idx] = ans_u[0]; + d_a[s+(t>>1)].v[in_chunk_idx+Chunk::elements_in_chunk] = ans_u[1]; + } + __syncthreads(); + +} + +/* + * This is the linear evaluation phase + */ +__device__ void InTB_LinearEvaluation( Chunk* const d_a, + const idx_t idx , + const idx_t chunkIdx , + const idx_t in_chunk_idx, + const len_t half_d_a_len){ + chunk_cell_t load[4]; + load[0] = d_a[chunkIdx].v[in_chunk_idx]; + load[1] = d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]; + load[2] = d_a[half_d_a_len+chunkIdx].v[in_chunk_idx]; + load[3] = d_a[half_d_a_len+chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]; + + DUP_ODD_BITS(load[0]); + DUP_ODD_BITS(load[1]); + + DUP_EVEN_BITS(d_a[chunkIdx].v[in_chunk_idx]); + DUP_EVEN_BITS(d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]); + __syncthreads(); + InTB_preMulExpand(d_a,d_linear_mul,1,idx,chunkIdx,in_chunk_idx); + InTB_Mult(d_a+(chunkIdx<<1),in_chunk_idx); + InTB_postMulShrink(d_a,idx,chunkIdx,in_chunk_idx); + + d_a[chunkIdx].v[in_chunk_idx] ^= load[0]; + d_a[chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk] ^= load[1]; + __syncthreads(); + + load[0]=load[2]; + load[2]=d_a[chunkIdx].v[in_chunk_idx]; + d_a[chunkIdx].v[in_chunk_idx] = load[0]; + + load[1]=load[3]; + load[3]=d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]; + d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk] = load[1]; + + DUP_ODD_BITS(load[0]); + DUP_ODD_BITS(load[1]); + + DUP_EVEN_BITS(d_a[chunkIdx].v[in_chunk_idx]); + DUP_EVEN_BITS(d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]); + __syncthreads(); + InTB_preMulExpand(d_a,d_linear_mul,1,idx,chunkIdx,in_chunk_idx); + InTB_Mult(d_a+(chunkIdx<<1),in_chunk_idx); + InTB_postMulShrink(d_a,idx,chunkIdx,in_chunk_idx); + + d_a[chunkIdx].v[in_chunk_idx] ^= load[0]; + d_a[chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk] ^= load[1]; + __syncthreads(); + + d_a[chunkIdx + half_d_a_len].v[in_chunk_idx] = d_a[chunkIdx].v[in_chunk_idx]; + d_a[chunkIdx + half_d_a_len].v[in_chunk_idx + Chunk::elements_in_chunk] = + d_a[chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk]; + + d_a[chunkIdx].v[in_chunk_idx]=load[2]; + d_a[chunkIdx].v[in_chunk_idx + Chunk::elements_in_chunk]=load[3]; + + __syncthreads(); +} + +/* + * This is the WFromUV operation in chunk (i.e. dim<5) + * dim = dim of subspace. + */ +__device__ void InTB_WFromUV_inChunk( Chunk* const d_a, + const Chunk * const subspace, + const len_t dim, + const idx_t idx, + const idx_t chunkIdx, + const idx_t in_chunk_idx, + const len_t half_d_a_len){ + /* + * 1)backup the WHOLE cell. + * 2)Multiply by subspace chunk (will nullify lower top). + * 3)Xor by it self when shifting right subspace-times. + * 4) Xor the load. + */ + chunk_cell_t load[4]; + /* + * Just backing up upper half + */ + load[2] = d_a[chunkIdx + half_d_a_len].v[in_chunk_idx]; + load[3] = d_a[chunkIdx + half_d_a_len].v[in_chunk_idx + Chunk::elements_in_chunk]; + + load[0] = d_a[chunkIdx].v[in_chunk_idx] ; + load[1] = d_a[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk] ; + load[0] ^= ((load[0]&(d_alter_mask[dim+1]))<<(1<>(1<>(1<>(1<>(1<=5. + * 1) d_a - shmem ptr. + * 2) subspace - global mem ptr of length (1<<(dim-1-Chunk::log_elements_in_chunk)) to a subspace of dim (dim-1). + * 3) dim - dimension of subspace. + * 4) idx - threadIdx. + */ +__device__ void InTB_WFromUV_outChunk( Chunk* const d_a, + const Chunk* const subspace, + const len_t dim, + const idx_t idx, + const len_t half_d_a_len){ + /* + * 1) Backup everything on register. + * 2) Multiply by subspace only bottom halves of the sub-polynomials. + */ + idx_t chunkIdx64[2]; + idx_t original_idx[2]; + original_idx[0] = ((idx& andMask(log_max_block_size)) >> Chunk::log_cells_in_chunk); + original_idx[1] = original_idx[0]+(max_block_size>>Chunk::log_cells_in_chunk); + chunkIdx64[0] = (((original_idx[0])>>(dim-Chunk::log_elements_in_chunk))<<(dim+1-Chunk::log_elements_in_chunk)) + + (original_idx[0] & andMask(dim-Chunk::log_elements_in_chunk)); + chunkIdx64[1] = (((original_idx[1])>>(dim-Chunk::log_elements_in_chunk))<<(dim+1-Chunk::log_elements_in_chunk)) + + (original_idx[1] & andMask(dim-Chunk::log_elements_in_chunk)); + idx_t in_chunk_idx64 = (idx) & andMask(Chunk::log_cells_in_chunk); + chunk_cell_t load[4]; + //Backing up everything on local memory. + load[0]= d_a[chunkIdx64[0]].v[in_chunk_idx64]; + load[1]= d_a[chunkIdx64[0] + (1<<(dim-Chunk::log_elements_in_chunk))].v[in_chunk_idx64]; + load[2] = d_a[chunkIdx64[1]].v[in_chunk_idx64]; + load[3] = d_a[chunkIdx64[1]+ (1<<(dim-Chunk::log_elements_in_chunk))].v[in_chunk_idx64]; + + __syncthreads(); + + //Preparing data for multiplication. + d_a[(original_idx[0])<<1].v[in_chunk_idx64]=load[1]; + d_a[(original_idx[1])<<1].v[in_chunk_idx64]=load[3]; + + //If my chunk should be multiplied, I load the relevant subspace chunk to the next chunk, so they will be multiplied. + //My chunk should be multiplied if my chunkIdx/(sizeOfSubspace in Chunk) is odd. + //chunkIdx is the same for all threads in the warp - so no divergence is possible. + d_a[(original_idx[0]<<1)+1].v[in_chunk_idx64]= + subspace[original_idx[0] & andMask(dim-Chunk::log_elements_in_chunk)].v[in_chunk_idx64]; + d_a[(original_idx[1]<<1)+1].v[in_chunk_idx64]= + subspace[original_idx[1] & andMask(dim-Chunk::log_elements_in_chunk)].v[in_chunk_idx64]; + __syncthreads(); + InTB_Mult(d_a+(((idx&andMask(log_max_block_size))>>Chunk::log_threads_in_chunk)<<1),idx&andMask(Chunk::log_threads_in_chunk)); + + chunk_cell_t t[2]; + t[0]= d_a[original_idx[0]<<1].v[in_chunk_idx64]; + t[1]= d_a[original_idx[1]<<1].v[in_chunk_idx64]; + __syncthreads(); + load[1]^=load[0]; + load[3]^=load[2]; + d_a[chunkIdx64[0]+(1<<(dim-Chunk::log_elements_in_chunk))].v[in_chunk_idx64] = t[0] ^ load[1]; + d_a[chunkIdx64[0]].v[in_chunk_idx64] = t[0] ^ load[0]; + d_a[chunkIdx64[1]+(1<<(dim-Chunk::log_elements_in_chunk))].v[in_chunk_idx64] = t[1] ^ load[3]; + d_a[chunkIdx64[1]].v[in_chunk_idx64] = t[1] ^ load[2]; + __syncthreads(); +} + +__global__ void +//__launch_bounds__(1<>Chunk::log_threads_in_chunk); + __shared__ Chunk s[half_d_a_len<<1]; + const idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; + const idx_t poly_idx = (idx >> log_max_block_size)<<(log_max_block_size+1-Chunk::log_elements_in_chunk); + const idx_t chunkIdx = (idx & andMask(log_max_block_size))>> Chunk::log_elements_in_chunk; + const idx_t in_chunk_idx = idx & andMask(Chunk::log_elements_in_chunk); + if(poly_idx+chunkIdx < poly_len){ + s[chunkIdx].v[in_chunk_idx] = d_a[(poly_idx) + chunkIdx].v[in_chunk_idx]; + s[chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk] = d_a[(poly_idx) + chunkIdx].v[in_chunk_idx+Chunk::elements_in_chunk]; + } + if(poly_idx+chunkIdx+half_d_a_len < poly_len){ + s[chunkIdx+half_d_a_len].v[in_chunk_idx] = d_a[(poly_idx) + chunkIdx + half_d_a_len].v[in_chunk_idx]; + s[chunkIdx+half_d_a_len].v[in_chunk_idx+Chunk::elements_in_chunk] = + d_a[(poly_idx) + chunkIdx + half_d_a_len].v[in_chunk_idx+Chunk::elements_in_chunk]; + } + + __syncthreads(); + idx_t i = dim; + for(; i > Chunk::log_elements_in_chunk ; --i){ + InTB_MultiExp(s,exps[i],1<<(i-Chunk::log_elements_in_chunk),idx,chunkIdx,in_chunk_idx,half_d_a_len); + //The problem is in TaylorExp - s is nullified! + InTB_TaylorExp(s,i,idx,idx & andMask(Chunk::log_cells_in_chunk),(idx&andMask(log_max_block_size)) >> Chunk::log_cells_in_chunk,half_d_a_len); + InTB_partition(s,i,idx,chunkIdx,in_chunk_idx,(i-Chunk::log_elements_in_chunk),half_d_a_len); + } + for(; i >= 2; --i){ + InTB_MultiExp(s,exps[i],1,idx,chunkIdx,in_chunk_idx,half_d_a_len); + InTB_TaylorExp(s,i,idx,idx & andMask(Chunk::log_cells_in_chunk),(idx&andMask(log_max_block_size)) >> Chunk::log_cells_in_chunk,half_d_a_len); + InTB_partition(s,i,idx,chunkIdx,in_chunk_idx,0,half_d_a_len); + } + InTB_LinearEvaluation(s,idx,chunkIdx,in_chunk_idx,half_d_a_len); + for(; i < Chunk::log_elements_in_chunk && ibasis.getSize(); + for(unsigned int i = 2 ; i <= dim && i<=Chunk::log_elements_in_chunk ; ++i){ + cudaMalloc(&(d_subspaces[i-1]),sizeof(Chunk)); + cudaMalloc(&(d_exps[i]),sizeof(Chunk)); + cudaMemcpy(d_subspaces[i-1],fft->gpu_subspace[fftSize-i],sizeof(Chunk),cudaMemcpyHostToDevice); + cudaMemcpy(d_exps[i],fft->gpu_exp[fftSize-i],sizeof(Chunk),cudaMemcpyHostToDevice); + } + for(unsigned int i = Chunk::log_elements_in_chunk+1 ; i <= dim ; ++i){ + cudaMalloc(&(d_subspaces[i-1]),sizeof(Chunk)*(1<<(i-1-Chunk::log_elements_in_chunk))); + cudaMalloc(&(d_exps[i]),sizeof(Chunk)*(1<<(i-Chunk::log_elements_in_chunk))); + cudaMemcpy(d_subspaces[i-1],fft->gpu_subspace[fftSize-i],sizeof(Chunk)*(1<<(i-1-Chunk::log_elements_in_chunk)),cudaMemcpyHostToDevice); + cudaMemcpy(d_exps[i],fft->gpu_exp[fftSize-i],sizeof(Chunk)*(1<<(i-Chunk::log_elements_in_chunk)),cudaMemcpyHostToDevice); + } + + cudaMemcpy(d_subspaces_allocated,d_subspaces,sizeof(Chunk*)*(log_max_block_size+2),cudaMemcpyHostToDevice); + cudaMemcpy(d_exps_allocated,d_exps,sizeof(Chunk*)*(log_max_block_size+2),cudaMemcpyHostToDevice); + + k_gpuFFT_InTB<<>>(p,d_subspaces_allocated,d_exps_allocated,dim,p_len); + for(unsigned int i = 2 ; i<= dim ; ++i){ + cudaFree(d_subspaces[i-1]); + cudaFree(d_exps[i]); + } + + cudaFree(d_subspaces_allocated); + cudaFree(d_exps_allocated); + } + void GPU_FFT::fft_gpu(const FFT* const fft,Polynomial* P){ + Chunk::setMod(); + setUpConstantMemory(fft); + Element* normalized_P = *P; + len_t p_len = 1<basis.getSize(); + len_t fixed_len = MAX((p_len),warp_size); + if(p_len < fixed_len) { + normalized_P = (Element*)malloc(sizeof(Element)*fixed_len); + memset(normalized_P,0,sizeof(Element)*fixed_len); + memcpy(normalized_P,*P,sizeof(Element)*p_len); + } + fixed_len >>= Chunk::log_elements_in_chunk; + p_len = fixed_len; + Elements_Chunk* d_p; + Chunk* d_chunk_P; + Chunk* d_chunk_P_cpy; + Chunk* d_swap; + cudaMalloc(&d_p,sizeof(Elements_Chunk)*(fixed_len)); + cudaMalloc(&d_chunk_P,sizeof(Chunk)*(fixed_len)); + cudaMemcpy(d_p,normalized_P,sizeof(Elements_Chunk)*(fixed_len),cudaMemcpyHostToDevice); + Chunk::normalToChunk((Elements_Chunk*)d_p,d_chunk_P,fixed_len,false); + cudaFree(d_p); + cudaMalloc(&d_chunk_P_cpy,sizeof(Chunk)*(fixed_len)); + len_t dim = fft->basis.getSize(); +#ifdef __MEASURE +#ifdef __GNUC__ + timespec start,end; + clock_gettime(CLOCK_REALTIME,&start); +#endif //#ifdef __GNUC__ +#endif //#ifdef __MEASURE + /* + * The Algorithm + */ + unsigned int i = dim; + for(; i > 1 ; --i){ + multiExponentiate_gpu(fft,d_chunk_P,p_len,i,d_chunk_P_cpy); + taylorExpansion_gpu(fft,d_chunk_P,p_len,i); + if(partition(d_chunk_P,d_chunk_P_cpy,p_len,i)){ + d_swap = d_chunk_P; + d_chunk_P=d_chunk_P_cpy; + d_chunk_P_cpy = d_swap; + } + } +// fft_gpu_InTB(i,fft,d_chunk_P,p_len); + linearEvaluation(d_chunk_P,d_chunk_P_cpy,p_len); + for(; i < dim ; ++i ){ + cudaMemcpy(d_chunk_P_cpy,fft->gpu_subspace[dim-1-i],sizeof(Chunk)<<(MAX((int)i-(int)Chunk::log_elements_in_chunk,0)),cudaMemcpyHostToDevice); + WFromUV(d_chunk_P,p_len,d_chunk_P_cpy,i); + } + /* + * End of algorithm + */ +#ifdef __MEASURE +#ifdef __GNUC__ + clock_gettime(CLOCK_REALTIME,&end); + double elapsed_secs = end.tv_sec - start.tv_sec + + ((double) (end.tv_nsec - start.tv_nsec)) / ((double) 1000000000); + std::cout << elapsed_secs << "GpU!!" <basis.getSize(); + len_t fixed_len = MAX((p_len),warp_size); + if(p_len < fixed_len) { + normalized_P = (Element*)malloc(sizeof(Element)*fixed_len); + memset(normalized_P,0,sizeof(Element)*fixed_len); + memcpy(normalized_P,*P,sizeof(Element)*p_len); + } + fixed_len >>= Chunk::log_elements_in_chunk; + p_len = fixed_len; + Elements_Chunk* d_p; + Chunk* d_chunk_P; + Chunk* d_chunk_P_cpy; + Chunk* d_swap; + cudaMalloc(&d_p,sizeof(Elements_Chunk)*(fixed_len)); + cudaMalloc(&d_chunk_P,sizeof(Chunk)*(fixed_len)); + cudaMemcpy(d_p,normalized_P,sizeof(Elements_Chunk)*(fixed_len),cudaMemcpyHostToDevice); + Chunk::normalToChunk((Elements_Chunk*)d_p,d_chunk_P,fixed_len,false); + cudaFree(d_p); + cudaMalloc(&d_chunk_P_cpy,sizeof(Chunk)*(fixed_len)); + len_t dim = fft->basis.getSize(); + +#ifdef __MEASURE +#ifdef __GNUC__ + timespec start,end; + clock_gettime(CLOCK_REALTIME,&start); +#endif //#ifdef __GNUC__ +#endif //#ifdef __MEASURE + /* + * The Algorithm + */ + unsigned int i = dim-1; + for(; i >= 1 ; --i ){ + cudaMemcpy(d_chunk_P_cpy,fft->gpu_subspace[dim-1-i],sizeof(Chunk)<<(MAX((int)i-(int)Chunk::log_elements_in_chunk,0)),cudaMemcpyHostToDevice); + UVFromW(d_chunk_P,p_len,d_chunk_P_cpy,i); + } + ilinearEvaluation(d_chunk_P,d_chunk_P_cpy,p_len); + for(i=2; i <= dim ; ++i){ + if(ipartition(d_chunk_P,d_chunk_P_cpy,p_len,i)){ + d_swap = d_chunk_P; + d_chunk_P=d_chunk_P_cpy; + d_chunk_P_cpy = d_swap; + } + itaylorExpansion_gpu(fft,d_chunk_P,p_len,i); + imultiExponentiate_gpu(fft,d_chunk_P,p_len,i,d_chunk_P_cpy); + } + /* + * End of algorithm + */ +#ifdef __MEASURE +#ifdef __GNUC__ + clock_gettime(CLOCK_REALTIME,&end); + double elapsed_secs = end.tv_sec - start.tv_sec + + ((double) (end.tv_nsec - start.tv_nsec)) / ((double) 1000000000); + std::cout << elapsed_secs << "GpU!!" <= ((p_len) << (Chunk::log_cells_in_chunk))) + return; + //Performs the rest of the expansion. + chunk_cell_t cell = chunk->v[in_chunk_idx]; + for(len_t i = 2; i<=t_dim && i <=Chunk::log_elements_in_chunk ; ++i){ + cell ^= (cell & (d_chunk_cell_mask[i]<<(1<<(i-1))))>>(1<<(i-2)); + cell ^= (cell & (d_chunk_cell_mask[i]<<((1<>(1<<(i-2)); + } + chunk->v[in_chunk_idx]= cell; +} +__global__ void k_itaylorExpansion_iteration_large(Chunk* d_chunk_P , len_t p_len , len_t t_dim,len_t i){ + idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; + idx_t in_chunk_idx = idx & (Chunk::cells_in_chunk-1); + /* + * flag = true if current sub-polynomial fits in a thread block. + */ + bool flag = (i-Chunk::log_elements_in_chunk <= log_max_block_size-Chunk::log_cells_in_chunk + 2); + if(idx >= ((p_len) << (Chunk::log_cells_in_chunk-2))) + return; + do { + len_t sub_len = (1<<(i-Chunk::log_elements_in_chunk)); + idx_t chunk_idx = idx >> (Chunk::log_cells_in_chunk); + chunk_idx = (chunk_idx /(sub_len>>2))*(sub_len) + ((chunk_idx) & ((sub_len>>2)-1)); + d_chunk_P[(sub_len>>2) + chunk_idx ].v[in_chunk_idx] ^= d_chunk_P[(sub_len>>1) + chunk_idx].v[in_chunk_idx]; + d_chunk_P[(sub_len>>1) + chunk_idx ].v[in_chunk_idx] ^= d_chunk_P[(3*(sub_len>>2)) + chunk_idx].v[in_chunk_idx]; + if(flag) + __syncthreads(); + ++i; + } while (i-Chunk::log_elements_in_chunk <= log_max_block_size-Chunk::log_cells_in_chunk + 2 && i<=t_dim); + //If number of threads needed is less than a thread block - we can just continue! +} +__global__ void k_itaylorExpansion_iteration_twoChunks(Chunk* d_chunk_P, len_t p_len, len_t t_dim){ + idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; + idx_t in_chunk_idx = idx & (Chunk::cells_in_chunk-1); + if(idx >= ((p_len) << (Chunk::log_cells_in_chunk-1))) + return; + idx_t chunk_idx= (idx >> (Chunk::log_cells_in_chunk) ) << 1; + d_chunk_P[(chunk_idx)].v[in_chunk_idx]^= (d_chunk_P[(chunk_idx)+1].v[in_chunk_idx]<<(Chunk::elements_in_chunk>>1)); + d_chunk_P[(chunk_idx)+1].v[in_chunk_idx]^= (d_chunk_P[(chunk_idx)+1].v[in_chunk_idx]>>(Chunk::elements_in_chunk>>1)); + +} +__global__ void k_itaylorExpansion_iteration_singleChunk(Chunk* d_chunk_P, len_t p_len, len_t t_dim){ + + idx_t idx = threadIdx.x + blockIdx.x*blockDim.x; + idx_t in_chunk_idx = idx & (Chunk::cells_in_chunk-1); + //Performs the rest of the expansion. + idx_t chunk_idx = (idx >> (Chunk::log_cells_in_chunk)); + itaylor_smaller_than_chunk(d_chunk_P+chunk_idx,in_chunk_idx,t_dim,p_len,idx); +} +void itaylorExpansion_iteration(const FFT* fft, Chunk * d_chunk_P , len_t p_len , len_t t_dim,len_t i){ + unsigned int threads; + dim3 blockSize(max_block_size,1,1); + if( i >= Chunk::log_elements_in_chunk + 2){ + threads = p_len<<(Chunk::log_cells_in_chunk-2); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_itaylorExpansion_iteration_large<<>>(d_chunk_P,p_len,t_dim,i); + } else if (i == Chunk::log_elements_in_chunk + 1){ + threads = p_len <<(Chunk::log_cells_in_chunk-1); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_itaylorExpansion_iteration_twoChunks<<>>(d_chunk_P,p_len,t_dim); + } else { + threads = p_len << Chunk::log_cells_in_chunk; + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_itaylorExpansion_iteration_singleChunk<<>>(d_chunk_P,p_len,t_dim); + } +// k_taylorExpansion_iteration<<>>(d_chunk_P,p_len,t_dim); +} +void GPU_FFT::itaylorExpansion_gpu(const FFT* fft, Chunk* d_chunk_P, len_t p_len , len_t t_dim){ + /* + * The sub-polynomial requires more than a single thread block. + */ + itaylorExpansion_iteration(fft,d_chunk_P,p_len,t_dim,2); + len_t i = Chunk::log_elements_in_chunk+1; + if(t_dim>=i){ + itaylorExpansion_iteration(fft,d_chunk_P,p_len,i,i); + ++i; + } + if(t_dim>=i){ + itaylorExpansion_iteration(fft,d_chunk_P,p_len,t_dim,i); + i = log_max_block_size+Chunk::log_elements_in_chunk+3-Chunk::log_cells_in_chunk; + } + + while(t_dim>=i){ + itaylorExpansion_iteration(fft,d_chunk_P,p_len,t_dim,i); + ++i; + } +} +/* + * inverse-partition + */ + + /* + * Input: + * 1) d_chunk_p - The polynomial on device's memory. + * 2) p_len - number of chunks in d_cunnk_p. + * 3) chunk_idx - the chunk current thread has to deal with. + * 4) in_chunk_idx - the number of the cell the current thread deals with. + * 5) t_dim - The dim of the original (input) subpolynomial. + * + * This function performs what a single thread does when performing the partition function, on a single chunk. + */ +__device__ void ipartition_in_chunk(Chunk* d_chunk_p, len_t p_len , idx_t in_chunk_idx, len_t t_dim){ + chunk_cell_t ans = d_chunk_p->v[in_chunk_idx]; + for(unsigned int i = MIN(t_dim,Chunk::log_elements_in_chunk) ; i >= 2 ; --i){ + ans = (ans & d_chunk_cell_mask[i]) | + (ans & (d_chunk_cell_mask[i]<<((1<<(i))-(1<<(i-2))))) | + (ans & (d_chunk_cell_mask[i]<<(1<<(i-1))))>>(1<<(i-2)) | + (ans & (d_chunk_cell_mask[i]<<(1<<(i-2))))<<(1<<(i-2)); + } + d_chunk_p->v[in_chunk_idx]=ans; +} + +//__device__ void ipartition_two_chunks(Chunk* d_chunk_p, idx_t in_chunk_idx){ +// chunk_cell_t ans[2]; +// chunk_cell_t load[2]; +// const chunk_cell_t mask = d_chunk_cell_mask[Chunk::log_elements_in_chunk+1]; +// load[0]=d_chunk_p->v[in_chunk_idx]; +// load[1]=d_chunk_p[1].v[in_chunk_idx]; +// ans[0]=(load[0] & mask) | ((load[1] & mask)<<(Chunk::elements_in_chunk>>1)); +// ans[1]=(load[1] & (mask<< (Chunk::elements_in_chunk>>1)) )| +// ((load[0] >> (Chunk::elements_in_chunk>>1)) & mask); +// d_chunk_p->v[in_chunk_idx] = ans[0]; +// d_chunk_p[1].v[in_chunk_idx] = ans[1]; +//} + +__device__ void ipartition_general(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, idx_t chunk_idx, idx_t in_chunk_idx, idx_t t_dim){ + if(chunk_idx & 1) + d_chunk_p_dst[chunk_idx].v[in_chunk_idx] = d_chunk_p_src[(chunk_idx>>1) + (1<<(t_dim-1))].v[in_chunk_idx]; + else + d_chunk_p_dst[chunk_idx].v[in_chunk_idx] = d_chunk_p_src[chunk_idx>>1].v[in_chunk_idx]; +} + +__global__ void k_ipartition_iteration_two_chunks(Chunk* d_chunk_p_src, len_t p_len){ + idx_t idx = threadIdx.x + blockIdx.x * blockDim.x; + idx_t chunk_idx = (idx >> Chunk::log_cells_in_chunk)<<1; + idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); + if(chunk_idx >= p_len){ + return; + } + partition_two_chunks(d_chunk_p_src+chunk_idx, in_chunk_idx); +} +__global__ void k_ipartition_iteration_in_chunk(Chunk* d_chunk_p_src, len_t p_len, len_t t_dim){ + idx_t idx = threadIdx.x + blockIdx.x * blockDim.x; + idx_t chunk_idx = idx >> Chunk::log_cells_in_chunk; + idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); + if(chunk_idx >= p_len){ + return; + } + ipartition_in_chunk(d_chunk_p_src+chunk_idx,p_len,in_chunk_idx,t_dim); +} +__global__ void k_ipartition_iteration_general(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, len_t p_len, len_t t_dim){ + idx_t idx = threadIdx.x + blockIdx.x * blockDim.x; + idx_t chunk_idx = idx >> Chunk::log_cells_in_chunk; + idx_t in_chunk_idx = idx & andMask(Chunk::log_cells_in_chunk); + idx_t base = chunk_idx ^ (chunk_idx & andMask(t_dim-Chunk::log_elements_in_chunk)); + chunk_idx &= andMask(t_dim-Chunk::log_elements_in_chunk); + if(base+chunk_idx >= p_len) + return; + ipartition_general(d_chunk_p_src+base,d_chunk_p_dst+base, chunk_idx, in_chunk_idx,t_dim-Chunk::log_elements_in_chunk ); +} +/* + * If t_dim > Chunk::log_elements_in_chunk+1 the result is written in dst, otherwise it will be written in src. + */ +bool GPU_FFT::ipartition(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, len_t p_len, len_t t_dim){ + len_t threads = p_len << Chunk::log_cells_in_chunk; + dim3 blockSize(max_block_size,1,1); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + bool flag = false; + if(t_dim > Chunk::log_elements_in_chunk+1){ + k_ipartition_iteration_general<<>>(d_chunk_p_src,d_chunk_p_dst,p_len,t_dim); + Chunk* swap; + swap = d_chunk_p_src; + d_chunk_p_src = d_chunk_p_dst; + d_chunk_p_dst = swap; + flag= true; + } + if(t_dim >= Chunk::log_elements_in_chunk +1){ + dim3 gridSizeTwoChunks(sizeCiel(threads>>1,max_block_size),1,1); + k_ipartition_iteration_two_chunks<<>>(d_chunk_p_src,p_len); + } + k_ipartition_iteration_in_chunk<<>>(d_chunk_p_src,p_len,t_dim); + return flag; +} +/* + * inverse linear evaluation + */ +__global__ void k_icopy_and_shift_vec(Chunk* d_chunk_src, Chunk* d_chunk_dst, len_t p_len) +{ + const idx_t shared_len = max_block_size>>Chunk::log_threads_in_chunk; + __shared__ Chunk c_shared[shared_len<<1]; + chunk_cell_t t; + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + const idx_t chunk_idx = (idx >> Chunk::log_threads_in_chunk); + + if(chunk_idx >= p_len){ + return; + } + + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + const idx_t shared_chunk_idx = ((idx & andMask(log_max_block_size)) >> (Chunk::log_threads_in_chunk)); + Chunk* my_shared_chunk = c_shared+(shared_chunk_idx<<1); + + for(unsigned int i =0 ; i < Chunk::ord ;i+=warp_size){ + my_shared_chunk->v[in_chunk_idx+i]=d_chunk_src[chunk_idx].v[in_chunk_idx+i]; + my_shared_chunk[1].v[in_chunk_idx+i]=d_ilinear_mul->v[in_chunk_idx+i]; + } + + chunk_cell_t tmp; +#pragma unroll + for(unsigned int i = 0 ; i < Element::ord ; i+=warp_size){ + tmp = my_shared_chunk->v[in_chunk_idx+i]; + t = (tmp& 0x55555555) ^ ((tmp & 0xaaaaaaaa)>>1); + my_shared_chunk->v[in_chunk_idx+i] = t^(t<<1); + d_chunk_dst[chunk_idx].v[in_chunk_idx+i] = (tmp & 0x55555555) ; + } + Chunk::clmul_by_chunk(my_shared_chunk[0],my_shared_chunk[1],in_chunk_idx,my_shared_chunk); + Chunk::chunk_reduce(d_chunk_src+chunk_idx,my_shared_chunk,in_chunk_idx); + for(unsigned int i = 0 ; i < Element::ord ; i+=warp_size){ + d_chunk_src[chunk_idx].v[in_chunk_idx+i] ^= d_chunk_dst[chunk_idx].v[in_chunk_idx+i]; + } + return; +} +void GPU_FFT::ilinearEvaluation(Chunk* d_chunk_p,Chunk* d_chunk_p_cpy, len_t p_len) +{ + len_t threads = p_len << Chunk::log_threads_in_chunk; + dim3 blockSize(max_block_size,1,1); + dim3 gridSize(sizeCiel(threads,max_block_size),1,1); + k_icopy_and_shift_vec<<>>(d_chunk_p,d_chunk_p_cpy,p_len); +} +/* + * UVFromW + */ + +__global__ void k_isubspaceMult_chunk_and_add(Chunk* d_a, Chunk* d_b , len_t log_elements_in_b ,len_t a_len ){ + idx_t idx = blockDim.x*blockIdx.x+threadIdx.x; + const idx_t chunk_idx = (idx >> Chunk::log_threads_in_chunk); + if(chunk_idx >= a_len){ + return; + } + const idx_t in_chunk_idx = idx & andMask(Chunk::log_threads_in_chunk); + Chunk::clmul_by_chunk_bShuffle_ixor_mask(d_a[chunk_idx],*d_b,in_chunk_idx,d_a[chunk_idx],1<=Chunk::log_elements_in_chunk){ + len_t threadsMul = p_len << (Chunk::log_threads_in_chunk-1); + len_t threadsAdd = p_len << (Chunk::log_cells_in_chunk-1); + dim3 block(multThreadsInBlock,1,1); + dim3 gridMul(sizeCiel(threadsMul,multThreadsInBlock),1,1); + dim3 gridAdd(sizeCiel(threadsAdd,multThreadsInBlock),1,1); + k_subspaceAdd_general<<>>(d_chunk_p,p_len,1<<(dim-Chunk::log_elements_in_chunk)); + k_subspaceMult_general<<>>(d_chunk_p, subspace, (1<<(dim-Chunk::log_elements_in_chunk)),p_len); + } else { + len_t threadsMul = p_len << Chunk::log_threads_in_chunk; + dim3 block(multThreadsInBlock,1,1); + dim3 gridMul(sizeCiel(threadsMul,multThreadsInBlock),1,1); + k_isubspaceMult_chunk_and_add<<>>(d_chunk_p,subspace, dim , p_len); + } +} + +} + +#endif diff --git a/algebra/FFT/src/GPU_FFT.cuh b/algebra/FFT/src/GPU_FFT.cuh new file mode 100644 index 0000000..aba35a1 --- /dev/null +++ b/algebra/FFT/src/GPU_FFT.cuh @@ -0,0 +1,36 @@ +/* + * GPU_FFT.h + * + * Created on: Aug 26, 2014 + * Author: matan + */ +#ifdef __GPU +#ifndef GPU_FFT_H_ +#define GPU_FFT_H_ + +#include "FFT.h" +namespace FFF{ + + class GPU_FFT{ + public: + static void multiExp_mult(len_t a_len, Chunk* d_a , Chunk* d_b , len_t b_len); + static void linearEvaluation(Chunk* d_chunk_p,Chunk* d_chunk_p_cpy, len_t p_len); + static void ilinearEvaluation(Chunk* d_chunk_p,Chunk* d_chunk_p_cpy, len_t p_len); + static void WFromUV(Chunk* d_chunk_p, len_t p_len, Chunk* subspace, len_t dim); + static void UVFromW(Chunk* d_chunk_p, len_t p_len, Chunk* subspace, len_t dim); + static void multiExponentiate_gpu(const FFT* fft,Chunk* d_chunk_P, len_t p_len, len_t dim, Chunk* d_res ); + static void imultiExponentiate_gpu(const FFT* fft,Chunk* d_chunk_P, len_t p_len, len_t dim, Chunk* d_exp ); + static void taylorExpansion_gpu(const FFT* fft, Chunk* d_chunk_P, len_t p_len , len_t dim); + static void itaylorExpansion_gpu(const FFT* fft, Chunk* d_chunk_P, len_t p_len , len_t dim); + static void setUpConstantMemory(const FFT* fft); + static bool partition(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, len_t p_len, len_t t_dim); + static bool ipartition(Chunk* d_chunk_p_src, Chunk* d_chunk_p_dst, len_t p_len, len_t t_dim); + static void fft_gpu(const FFT* fft,Polynomial* P); + static void ifft_gpu(const FFT* const fft,Polynomial* P); + static void fft_gpu_InTB(len_t dim, const FFT* const fft, Chunk* p, len_t p_len); + }; +} + + +#endif /* GPU_FFT_H_ */ +#endif // #ifdef __GPU diff --git a/algebra/FFT/src/Polynomials.cpp b/algebra/FFT/src/Polynomials.cpp new file mode 100644 index 0000000..02bae0d --- /dev/null +++ b/algebra/FFT/src/Polynomials.cpp @@ -0,0 +1,96 @@ +/* + * Polynomials.cpp + * + * Created on: Jul 2, 2014 + * Author: matan + */ + +#include "Polynomials.h" +#include + +namespace FFF { + void Polynomials::taylorExpansionIteration(Polynomial p, len_t l){ + Element::vecXor((cell_t*)&p[l>>1],(cell_t*)&p[3*(l>>2)],Element::element_len*(l>>2)); + Element::vecXor((cell_t*)&p[(l>>2)],(cell_t*)&p[(l>>1)],Element::element_len*(l>>2)); + } + void Polynomials::i_taylorExpansionIteration(Polynomial p, len_t l){ + Element::vecXor((cell_t*)&p[(l>>2)],(cell_t*)&p[(l>>1)],Element::element_len*(l>>2)); + Element::vecXor((cell_t*)&p[l>>1],(cell_t*)&p[3*(l>>2)],Element::element_len*(l>>2)); + } + void Polynomials::taylorExpansionIterationOMP(Polynomial p, len_t l){ +#pragma omp parallel for schedule(guided) + for(idx_t i = 0 ; i < (l>>2) ; ++i){ + Element::c_add(p[(l>>1)+i],p[3*(l>>2)+i],p[(l>>1)+i]); + Element::c_add(p[(l>>2)+i],p[(l>>1)+i],p[(l>>2)+i]); + } + } + void Polynomials::i_taylorExpansionIterationOMP(Polynomial p, len_t l){ +#pragma omp parallel for + for(idx_t i = 0 ; i < (l>>2) ; ++i){ + Element::c_add(p[(l>>2)+i],p[(l>>1)+i],p[(l>>2)+i]); + Element::c_add(p[(l>>1)+i],p[3*(l>>2)+i],p[(l>>1)+i]); + } + } + void Polynomials::taylorExpansion(Polynomial p, len_t log_len){ + len_t len=1< 2 ; i>>=1 ){ + for(int i_p = len-i; i_p>=0 ;i_p-=i) + taylorExpansionIteration((Element*)&p[i_p],i); + } + } + void Polynomials::i_taylorExpansion(Polynomial p, len_t log_len){ + len_t len=1< len/omp_max_threads ; i>>=1 ){ + for(int i_p = len-i; i_p>=0 ;i_p-=i) + taylorExpansionIterationOMP((Element*)&p[i_p],i); + } + for(;i>2;i>>=1){ +#pragma omp parallel for + for(int i_p = len-i ; i_p >=0 ; i_p-=i) + taylorExpansionIteration((Element*)&p[i_p],i); + } + } + void Polynomials::i_taylorExpansionOMP(Polynomial p,len_t log_len){ + len_t len=1<=0 ; i_p-=i) + i_taylorExpansionIteration((Element*)&p[i_p],i); + } + for(; i <= len ; i<<=1 ){ + for(int i_p = len-i; i_p>=0 ;i_p-=i) + i_taylorExpansionIterationOMP((Element*)&p[i_p],i); + } + } + + void Polynomials::printPolynomial(Polynomial p, len_t p_len){ + bool printed = false; + for(unsigned int i = 0 ; i < p_len ; ++i){ + if(!Element::c_isZero(p[i])){ + Element::printElement(p[i]); + std::cout<<"x^"< +#include + +namespace Algebra { + + + /** + * As described above, a class for general Affine polynomials. + */ + class AffinePolynomial : public UnivariatePolynomialInterface{ + + public: + + /** Constructor **/ + AffinePolynomial(const std::vector& coefficients, const FieldElement& constantFactor); + + /** + * @brief return a clone of the current polynomial + * @return a unique_ptr of PolynomialInterface, + * representing a polynomial equivalent to current + */ + std::unique_ptr clone()const; + + /** The function evaluates the Affine polynomial at a given point and returns the result. */ + FieldElement eval(const FieldElement& x)const; + + //return the i'th coefficient of this polynomial + FieldElement getCoefficient(const unsigned int i)const; + + PolynomialDegree getDegree() const; + + void multiplyByConstant(const FieldElement& factor); + + /** Class Destructor */ + virtual ~AffinePolynomial(){}; + + private: + std::vector coefficients_; //coefficients[i] = coefficient c of the monomial c*x^(2^i) + FieldElement constantFactor_; //The constant factor of the vanishing polynomial, relevant for affine subspaces and not linear. + size_t constantFactor_intRep_; + + std::array polyMat_; //the matrix corresponding to evaluating this poly (not including adding constantFactor) + /**updates the polyMat field to contain correct evaluation matrix*/ + void computeMat() ; + + + }; + + +}// of namespace Algebra + +#endif //PCPCD_REFACTORING_ALGEBRA_AFFINEPOLYNOMIAL_HPP_ diff --git a/algebra/algebralib/headers/algebraLib/AffineSpace.hpp b/algebra/algebralib/headers/algebraLib/AffineSpace.hpp new file mode 100644 index 0000000..52d19af --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/AffineSpace.hpp @@ -0,0 +1,24 @@ +#include +#include "FieldElement.hpp" + +#ifndef __AFFINE_SPACE_HPP__ +#define __AFFINE_SPACE_HPP__ + +namespace Algebra{ + +class AffineSpace{ +public: + typedef std::vector orderedBasis_t; + AffineSpace(const orderedBasis_t& basis, const FieldElement& affineShift = zero()); + const orderedBasis_t& getBasis()const; + const FieldElement& getAffineShift()const; + unsigned long long size()const; + FieldElement getElementByIndex(const unsigned long long idx)const; +private: + const std::vector basis_; + const FieldElement shift_; +}; + +} // namespace Algebra + +#endif //#ifndef __AFFINE_SPACE_HPP__ diff --git a/algebra/algebralib/headers/algebraLib/BitExtract.hpp b/algebra/algebralib/headers/algebraLib/BitExtract.hpp new file mode 100644 index 0000000..2d099b4 --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/BitExtract.hpp @@ -0,0 +1,32 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ALGEBRALIB_TRACE_HPP_ +#define ALGEBRALIB_TRACE_HPP_ + +#include +#include + +namespace Algebra { + +#define invExtrArrSize 27 +class invExtrType { + private: + static const FieldElement invExtrArr[invExtrArrSize]; + public: + const FieldElement& operator[](const int i)const; +}; +extern const invExtrType invExtrConsts; + +typedef std::array matBitExt_t; +matBitExt_t matForBitExtr(const int bitNum); +FieldElement extractBit(const FieldElement& elem, const int bitNum, const std::array& invExtrMat); +FieldElement extractBit(const FieldElement& elem, const int bitNum); + +} // namespace Algebra + +#endif // ALGEBRALIB_TRACE_HPP_ diff --git a/algebra/algebralib/headers/algebraLib/CircuitPolynomial.hpp b/algebra/algebralib/headers/algebraLib/CircuitPolynomial.hpp new file mode 100644 index 0000000..21a1b0c --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/CircuitPolynomial.hpp @@ -0,0 +1,60 @@ +#ifndef ALGEBRA_CIRCUITPOLYNOMIAL_HPP_ +#define ALGEBRA_CIRCUITPOLYNOMIAL_HPP_ + +#include +#include +#include +#include +#include + +using std::vector; +using std::string; + +namespace Algebra{ + +class CircuitPolynomial; +class SelectorSum; + +enum class PolynomialOperation{ + ADD, + MUL, + PROD, + SELECTORSUM +}; + +class CircuitPolynomial : public PolynomialInterface{ +private: + PolynomialOperation operation_; + std::shared_ptr S_; + vector input_; + vector factors_; +public: + CircuitPolynomial() : operation_(PolynomialOperation::PROD), input_(), factors_(1, LinearCombination(Algebra::zero())){} + CircuitPolynomial(const FElem& val) : operation_(PolynomialOperation::PROD), input_(), factors_(1, LinearCombination(val)) {} + CircuitPolynomial(const Variable& var) : operation_(PolynomialOperation::PROD), input_(), factors_(1, LinearCombination(var)) {} + CircuitPolynomial(const LinearTerm& linearTerm); + CircuitPolynomial(const LinearCombination& linearCombination) : operation_(PolynomialOperation::PROD), input_(), factors_(1, linearCombination) {} + CircuitPolynomial(const SelectorSum& S); + CircuitPolynomial(const vector& factors); + + bool isZero() const; + CircuitPolynomial& operator+=(const CircuitPolynomial& other); + CircuitPolynomial& operator*=(const CircuitPolynomial& other); + PolynomialOperation getOperation() const{ return operation_; } + string asString() const; + string asStringNewIndex(map& translate, bool isDegreePrint) const; + void getConsts(std::map& FElemMap) const; + FElem eval(const std::vector& assignment) const; + FElem eval(const VariableAssignment& assignment) const; + bool isSatisfied(const VariableAssignment& assignment) const; + Variable::set getUsedVariables() const; + void setNewIndices(std::map& old2New); + PolynomialDegree getDegreeBound(const std::vector& inputDegrees) const; + bool isEffectiveInput(const size_t varId) const; + std::unique_ptr clone() const { return std::unique_ptr(new CircuitPolynomial(*this)); } + size_t numVars()const; + ~CircuitPolynomial(){}; +}; + +} +#endif // ALGEBRA_CIRCUITPOLYNOMIAL_HPP_ diff --git a/algebra/algebralib/headers/algebraLib/ErrorHandling.hpp b/algebra/algebralib/headers/algebraLib/ErrorHandling.hpp new file mode 100644 index 0000000..d1f4089 --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/ErrorHandling.hpp @@ -0,0 +1,71 @@ +/** @file +***************************************************************************** +Common functionality needed by many components. +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#ifndef ALGEBRALIB_ERRORHANDALING_HPP_ +#define ALGEBRALIB_ERRORHANDALING_HPP_ + +#include +#include +#include +#include +#include + +#ifndef _MSC_VER // emulate the MSVC-specific sprintf_s using the standard snprintf +#define sprintf_s snprintf //TODO: sprintf_s!=snprintf (http://blog.verg.es/2008/09/sprintfs-is-not-snprintf.html) +#endif + +#ifdef _DEBUG // MSVC Debug build +#define DEBUG // gcc Debug flag +#endif + +namespace Algebra{ + // someday, if/when MSVC supports C++0x variadic templates, change FMT in release version to the + // following in order to increase efficiency: + // #define GADGETLIB3_FMT(...) "" + ::std::string ALGEBRALIB_FMT(const char* format, ...); + + + /********************************************************/ + /******************* Error Handling *********************/ + /********************************************************/ + + // declare a function as never returning, to quiet down "control reaches end of non-void function" warnings + #if defined(_MSC_VER) // VisualC++ + #define __noreturn _declspec(noreturn) + #elif defined(__GNUC__) + #define __noreturn __attribute__((noreturn)) + #else + #define __noreturn + #endif + + /** + * The ErrorHandling class containimplements the functionality of displaying the content of error + * messages (including content of call stack when error happened), and exiting the program. + */ + class ErrorHandling { + public: + static void __noreturn fatalError(const ::std::string& msg); + static void __noreturn fatalError(const std::stringstream& msg); + static void printStacktrace(); + + }; + +#define ALGEBRALIB_FATAL(msg) do { \ + ::std::stringstream msgStream; \ + msgStream << msg << " (In file " << __FILE__ << " line " << __LINE__ << ".)"; \ + ErrorHandling::fatalError(msgStream.str()); \ + } while (0) + + // TODO change GADGETLIB_ASSERT to not run in debug +#define ALGEBRALIB_ASSERT(predicate, msg) if(!(bool(predicate))) ALGEBRALIB_FATAL(msg); + + +} // namespace Algebra + +#endif // ALGEBRALIB_ERRORHANDALING_HPP_ diff --git a/algebra/algebralib/headers/algebraLib/FFT.hpp b/algebra/algebralib/headers/algebraLib/FFT.hpp new file mode 100644 index 0000000..dff8afc --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/FFT.hpp @@ -0,0 +1,58 @@ +#ifndef _ALGEBRALIB_FFT__HPP +#define _ALGEBRALIB_FFT__HPP + +#include "FieldElement.hpp" +#include + +namespace Algebra{ + +/** + * Calculates the inverse FFT (a specific kind of interpolation) + * Given the basis size is \f$n\f$ the expected size of polyEvaluation is \f$ 2^n \f$ + * it finds the single polynomial \f$p \in \mathbb{F}[x]\f$ of degree at most \f$ 2^n -1 \f$ such that + * for any \f$ a_0,a_1,\dots,a_{n-1} \in \{0,1\} \f$ it holds that: + * \f$ p( \sum_{i=0}^{i=n-1} a_i \cdot b_i ) = v_{\sum_{i=0}^{i=n-1} a_i \cdot 2_i} \f$ + * where \f$ b_i \f$ is the i'th element of the ordered basis + * and \f$ v_i \f$ is the i'th element of the polyEvaluation + * + * The return value is the coefficients vector of the polynomial \f$ p \f$ + */ +std::vector IFFT(const std::vector& polyEvaluation, const std::vector& orderedBasis, const FieldElement& shift); + +/** + * IFFT, but the result is stored in the input polyEvaluation + */ +void IFFT_inplace(FieldElement* polyEvaluation, const std::vector& orderedBasis, const FieldElement& shift); + +/** + * Calculates the FFT (a specific kind of fast evaluation of a polynomial over a vector space) + * Given the basis size is \f$n\f$ it evaluates \f$poly\f$ over the space spenned be the basis + * The result ordered in the following way: + * For any \f$ a_0,a_1,\dots,a_{n-1} \in \{0,1\} \f$ it holds that: + * \f$ poly( \sum_{i=0}^{i=n-1} a_i \cdot b_i ) = r_{\sum_{i=0}^{i=n-1} a_i \cdot 2_i} \f$ + * where \f$ b_i \f$ is the i'th element of the ordered basis + * and \f$ r_i \f$ is the i'th element of the result + * + * The return value is the coefficients vector of the polynomial \f$ p \f$ + */ +std::vector FFT(const std::vector& poly, const std::vector& orderedBasis, const FieldElement& shift); + +/** + * FFT, but the result is stored in the input poly + */ +void FFT_inplace(FieldElement* poly, const std::vector& orderedBasis, const FieldElement& shift, const size_t polyDeg_ceilLog); + +/** + * Low Degree Extension. + * Same as FFT(IFFT(polyEvaluations,orderedBasis_src),orderedBasis_dst) + */ +std::vector LDE(const std::vector& polyEvaluation, const std::vector& orderedBasis_src, const FieldElement& shift_src, const std::vector& orderedBasis_dst, const FieldElement& shift_dst); + +/** + * LDE, but the result is stored in the input polyEvaluation + */ +void LDE_inplace(FieldElement* polyEvaluation, const std::vector& orderedBasis_src, const FieldElement& shift_src, const std::vector& orderedBasis_dst, const FieldElement& shift_dst); + +} //namespace Algebra + +#endif // _ALGEBRALIB_FFT__HPP diff --git a/algebra/algebralib/headers/algebraLib/FieldElement.hpp b/algebra/algebralib/headers/algebraLib/FieldElement.hpp new file mode 100644 index 0000000..0981c4d --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/FieldElement.hpp @@ -0,0 +1,272 @@ +#include +#include +#include +#include + +#ifndef __Field_Element_HPP__ +#define __Field_Element_HPP__ + +namespace Algebra { + +const int ExtensionDegree = 64; + +/* c++11 pseudorandom number generator */ +extern std::mt19937_64 rng; + +/** +* @class FieldElement +*/ + +class FieldElement { +public: + + /** + * @brief Default constructor + */ + FieldElement() :element_(){}; + + /** + * @brief conversion construction from FFF element + */ + explicit FieldElement(const FFF::Element& e) : element_(e){}; + + /** + * @brief conversion to FFF::Element + */ + explicit operator const FFF::Element()const{ return element_; } + + /** + * @brief copy constructor + */ + FieldElement(const FieldElement& src) : element_(src.element_){}; + + + /** + * inverse + */ + + inline FieldElement inverse()const{ + FieldElement res; + FFF::Element dummy = element_; + FFF::Element::c_inv(dummy,res.element_); + return res; + } + + //added by Ariel + inline static FieldElement sqr(const FieldElement& x){ + return x*x; + } + + /** Bunch of operators */ + friend FieldElement operator+(const FieldElement&, const FieldElement&); + friend FieldElement& operator+=(FieldElement&, const FieldElement&); + friend FieldElement operator-(const FieldElement&, const FieldElement&); + friend FieldElement operator/(const FieldElement&, const FieldElement&); + friend FieldElement operator*(const FieldElement&, const FieldElement&); + friend FieldElement& operator*=(FieldElement&, const FieldElement&); + friend bool operator==(const FieldElement&, const FieldElement&); + friend bool operator!=(const FieldElement&, const FieldElement&); + //friend bool operator<(const FieldElement&, const FieldElement&); + friend FieldElement& operator-=(FieldElement&, const FieldElement&); + friend FieldElement& operator/=(FieldElement&, const FieldElement&); + friend const FieldElement zero(); + friend const FieldElement one(); + friend const FieldElement xFE(); + + friend FieldElement power(const FieldElement&, long exponent); + friend std::ostream& operator<<(std::ostream&, const FieldElement&); + friend bool compareFieldElements(const FieldElement& e1, const FieldElement& e2); + friend FieldElement mapIntegerToFieldElement(const size_t shift, const size_t numBits, size_t numToWrite); + friend size_t mapFieldElementToInteger(const size_t shift, const size_t numBits, const FieldElement& e); + + /** + * element_ as string + */ + ::std::string asString() const; + +private: + + const static short numBits = 64; + FFF::Element element_; /**< @brief instance state */ + +}; + +//Arithmetic operators +inline FieldElement operator/(const FieldElement& a, const FieldElement& b){ + return a*b.inverse(); +} + +inline FieldElement& operator/=(FieldElement& x, const FieldElement& b) +{ + x = x /b; return x; +} + +inline FieldElement operator+(const FieldElement& a, const FieldElement& b) +{ + FieldElement res; + FFF::Element::c_add(a.element_,b.element_,res.element_); + return res; +} + +inline FieldElement& operator+=(FieldElement& x, const FieldElement& b) +{ + x = x+b; return x; +} + +inline FieldElement operator-(const FieldElement& a, const FieldElement& b) +{ + return a+b; +} + +inline FieldElement& operator-=(FieldElement& x, const FieldElement& b) +{ + x = x - b; return x; +} + +inline FieldElement operator*(const FieldElement& a, const FieldElement& b) +{ + FieldElement res; + FFF::Element::c_mul(a.element_,b.element_,res.element_); + return res; +} + +inline FieldElement& operator*=(FieldElement& x, const FieldElement& b) +{ + x= x*b; return x; +} + +inline bool operator==(const FieldElement& a, const FieldElement& b) +{ + return a.element_.c[0] == b.element_.c[0]; +} + +inline bool operator!=(const FieldElement& a, const FieldElement& b) +{ + return a.element_.c[0] != b.element_.c[0]; +} + +inline FieldElement power(const FieldElement& a, long e) +{ + FieldElement res; + FFF::Element::c_exp(a.element_,e,res.element_); + return res; +} + +/** + * @brief An order on field elements + * This exists only in order to be able to keep + * field elements in an STL set, hance the order + * is arbitrary, as long as it really is an order. + * In this case lexicographically, representing each + * FieldElement as vector of bytes + */ +inline bool compareFieldElements(const FieldElement& e1, const FieldElement& e2){ + return e1.element_.c[0] > e2.element_.c[0]; +} + +struct classCompElements{ + bool operator()(const FieldElement& e1, const FieldElement& e2)const{ + return compareFieldElements(e1,e2); + } +}; + +/** + * Inverts a vector of field elements point-wise + * Does it in a more efficient way than the trivial solution. + * As division is known to be time consuming, in the case of n elements, + * instead of doing n division, only a single division operation is executed, + * and less than 3n multiplications. + */ +std::vector invertPointwise(const std::vector& elems); + +/** + * ElementsSet : just an STL set of field elements + * @note it is defined specifically here because + * there is no natural order on field elements, + * but an STL set requires an order on its values. + * the generator generates an empty set of elements + * with some arbitrary order + */ +typedef std::set elementsSet_t; +elementsSet_t getStandartBasis(const unsigned short basisSize); +std::vector getStandartOrderedBasis(const unsigned short basisSize); +/** Functions that generate field elements from integers **/ + +/** + * Let "numToWrite" = \f$ 2^0 b_0 + 2^1 b_1 + \dots + 2^k b_k \f$ + * Lets name "shift" as S , and "numBits" as N + * + * This functions generated the field element that + * as a polynomial over GF(2) might be represented as: + * \f$ x^{s} b_0 + x^{s+1} b_1 + \dots x^{s+n-1} b_{n-1} \f$ + **/ +inline FieldElement mapIntegerToFieldElement(const size_t shift, const size_t numBits, size_t numToWrite){ + FieldElement res; + size_t numToWrite_masked = numToWrite; + if (numBits < 64){ + const size_t mask = (size_t(1) << numBits) - 1; + numToWrite_masked = numToWrite & mask; + } + res.element_.c[0] = (numToWrite_masked) << shift; + + return res; +} + +/** + * Let "e" = \f$ x^{0} b_0 + x^{1} b_1 + x^{2} b_{2} \dots \f$ + * (as a polynomial over GF(2) and powers of "x" as the standard basis) + * Lets name "shift" as S , and "numBits" as N + * + * The returned value is the integer \f$ 2^0 b_{S} + 2^1 b_{S+1} + \dots + 2^k b_{S+N-1} \f$ + + **/ + +inline size_t mapFieldElementToInteger(const size_t shift, const size_t numBits, const FieldElement& e){ + size_t result = (e.element_.c[0]>>shift); + if (numBits < 64){ + const size_t mask = (size_t(1) << numBits) - 1; + result &= mask; + } + + return result; + } + +/** Constants for 0 and 1 */ + +inline const FieldElement zero(){ + const static FieldElement ZERO = mapIntegerToFieldElement(0,0,0); + return ZERO; +} + +inline const FieldElement one(){ + const static FieldElement ONE = mapIntegerToFieldElement(0,1,1); + return ONE; +} + +inline const FieldElement xFE() { + const static FieldElement X = mapIntegerToFieldElement(1,1,1); + return X; +} + +/** Functions that return random elements */ +FieldElement generateRandom(); + +/** + * Let \f$ B=(B_0, B_1, \dots , B_n) \f$ be the ordered basis + * and let \f$ S \f$ be the affine shift. + * This mapping maps the integer \f$ \sum_{i=0}^n b_i \cdot 2^i \f$ where \f$ \forall i : b_i \in \{0,1\} \f$ + * to the element \f$ S + \sum_{i=0}^n b_i \cdot B_i \f$ + */ +FieldElement getSpaceElementByIndex(const std::vector& orderedBasis, const FieldElement& affineShift, const size_t index); + +/** + * Let \f$ B=(B_0, B_1, \dots , B_n) \f$ be the ordered basis + * and let \f$ S \f$ be the affine shift. + * This mapping maps the element \f$ S + \sum_{i=0}^n b_i \cdot B_i \f$ where \f$ \forall i : b_i \in \{0,1\} \f$ + * to the integer \f$ \sum_{i=0}^n b_i \cdot 2^i \f$ + */ +size_t getSpaceIndexOfElement(const std::vector& orderedBasis, const FieldElement& affineShift, const FieldElement& e); + +} //namespace Algebra + +#endif // __Field_Element_HPP__ diff --git a/algebra/algebralib/headers/algebraLib/LinearPolynomial.hpp b/algebra/algebralib/headers/algebraLib/LinearPolynomial.hpp new file mode 100644 index 0000000..747f7de --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/LinearPolynomial.hpp @@ -0,0 +1,62 @@ +#include "PolynomialInterface.hpp" + +#ifndef LINEAR_POLYNOMIAL_HPP__ +#define LINEAR_POLYNOMIAL_HPP__ + +namespace Algebra{ + +class LinearPolynomial : public UnivariatePolynomialInterface { + +public: + + LinearPolynomial(const FieldElement& coeff0, const FieldElement& coeff1) : coefficient0_(coeff0),coefficient1_(coeff1){}; + + /** + * @brief return a clone of the current polynomial + * @return a unique_ptr of PolynomialInterface, + * representing a polynomial equivalent to current + */ + std::unique_ptr clone()const; + + /** + * @brief Evaluates the polynomial instance using specific element + * @param x the assignment + * @return P(x) + */ + FieldElement eval(const FieldElement& x)const{ + return coefficient0_ + x*coefficient1_; + } + + /** + * @brief returns the value of the coefficient of \f$x^\text{index}\f$ + * @param index the power of \f$x\f$ the coefficient multiplies + * @return cefficient the value of the coefficient + */ + FieldElement getCoefficient(const unsigned int index)const; + + /** + * @brief returns the degree of the polynomial + * @return polynomial degree + */ + PolynomialDegree getDegree()const; + + /** + * @brief computes the composition polynomial + * @param p input linear polynomial + * @return The composition \f$this \circ p\f$ such that + * \f$(this \circ p)(x) = this(p(x))\f$ + */ + LinearPolynomial compose(const LinearPolynomial& p)const; + +private: + const FieldElement coefficient0_; + const FieldElement coefficient1_; + +}; + +bool operator==(const LinearPolynomial& p1, const LinearPolynomial& p2); +LinearPolynomial operator+(const LinearPolynomial& p, const FieldElement& c); + +} //namespace Algebra + +#endif // LINEAR_POLYNOMIAL_HPP__ diff --git a/algebra/algebralib/headers/algebraLib/LinearizedPolynomial.hpp b/algebra/algebralib/headers/algebraLib/LinearizedPolynomial.hpp new file mode 100644 index 0000000..6391579 --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/LinearizedPolynomial.hpp @@ -0,0 +1,29 @@ +/********************************************** LinearizedPolynomials.hpp ************************************************/ +/** +* @file. +* +* A class of (strictly) linearized polynomials - i.e., +univariate polynomials whose non-zero coefficients are only of monomials of the form x^{2^i}, +and whose constant coefficient is zero. +* +*/ +/************************************************************************************************************/ +#ifndef LINEARIZED_POLYNOMIAL_HPP_ +#define LINEARIZED_POLYNOMIAL_HPP_ + + +#include "AffinePolynomial.hpp" +namespace Algebra { +class LinearizedPolynomial : public AffinePolynomial{ + + public: + /** Class Constructor - Assignes the given coefficient array to the newly created polynomial. + Assigns the constant factor of the poly to be 0. + */ + LinearizedPolynomial(std::vector coefficients) :AffinePolynomial(coefficients, zero()){}; + }; + + +}// of namespace Algebra + +#endif diff --git a/algebra/algebralib/headers/algebraLib/MappingsSys.hpp b/algebra/algebralib/headers/algebraLib/MappingsSys.hpp new file mode 100644 index 0000000..28226fa --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/MappingsSys.hpp @@ -0,0 +1,32 @@ +#include "FieldElement.hpp" +#include +#include + +#ifndef MAPPINGS_SYS_HPP__ +#define MAPPINGS_SYS_HPP__ +namespace Algebra{ + +class multivarFunc{ +public: + virtual ~multivarFunc(){}; + virtual size_t numVars() const = 0; + virtual Algebra::FieldElement eval(const std::vector& assignment) const = 0; +}; + +class MappingsSys{ +public: + virtual ~MappingsSys(){}; + virtual MappingsSys* clone() const = 0; + + virtual size_t numVars() const = 0; + virtual size_t numMappings() const = 0; + + // assignment length expected is numVars + virtual std::vector eval(const std::vector& assignment) const = 0; + + // num coefficients expected should be numMappings + virtual multivarFunc* getLinearComb(const std::vector& coeffs)const; +}; // class MappingsSys + +} //namespace Algebra +#endif //MAPPINGS_SYS_HPP__ diff --git a/algebra/algebralib/headers/algebraLib/PolynomialDegree.hpp b/algebra/algebralib/headers/algebraLib/PolynomialDegree.hpp new file mode 100644 index 0000000..8bc3f0a --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/PolynomialDegree.hpp @@ -0,0 +1,75 @@ +#ifndef __PolynomialDegree_HPP +#define __PolynomialDegree_HPP + +namespace Algebra { + +/** + * @class PolynomialDegree + * @brief The degree of a polynomial can + * be a natural integer, or \f$ -\infty\f$ + * (only for the zero polynomial). + * There are some arithmetic expectations + * from the degree, for example, the degree + * of a product of polynomials is the summary + * of their degrees. + * Such an equality can not be defined using + * integers only. + * This class should provide a representation + * for polynomial degree with implementation + * for arithmetic operators that provides + * expected results + */ +class PolynomialDegree { +public: + typedef long long integral_t; + /** + * @brief Constructor of degree from an integer + * @param degree, if non-negative construct a + * PolynomialDegree with same value, otherwise + * constructs the \f$ -\infty \f$ degree + */ + explicit PolynomialDegree(const integral_t degree); + + /** + * Construct the degree for -infinity + */ + static PolynomialDegree getZeroPolyDegree(){ + return PolynomialDegree(-1); + } + + /** + * @brief Checks if the degree is an integer (not \f$ -\infty \f$) + * @return True iff the degree is an integer + */ + bool isInteger()const; + + /** + * @brief conversion to int + * @return The degree if it is an integer, a negative value otherwise + */ + explicit operator integral_t()const; + + /** + * checks if a polynomial of such degree is constant + */ + bool isConstantPolyDeg()const{ + return (degree_ < 1); + } + +private: + integral_t degree_; +}; + +bool operator<(const PolynomialDegree& d1, const PolynomialDegree& d2); +bool operator==(const PolynomialDegree& d1, const PolynomialDegree& d2); +bool operator!=(const PolynomialDegree& d1, const PolynomialDegree& d2); + +PolynomialDegree degreeOfProduct(const PolynomialDegree& d1, const PolynomialDegree& d2); + +//Return the degree bound of polyA(polyB) +PolynomialDegree degreeOfComposition(const PolynomialDegree& polyA_deg, const PolynomialDegree& polyB_deg); + +} // namespace Algebra + + +#endif // __PolynomialDegree_HPP diff --git a/algebra/algebralib/headers/algebraLib/PolynomialInterface.hpp b/algebra/algebralib/headers/algebraLib/PolynomialInterface.hpp new file mode 100644 index 0000000..8330fe8 --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/PolynomialInterface.hpp @@ -0,0 +1,183 @@ +#ifndef __POLYNOMIAL_INTERFACE_HPP +#define __POLYNOMIAL_INTERFACE_HPP + +#include "FieldElement.hpp" +#include "PolynomialDegree.hpp" +#include "FFT.hpp" +#include "MappingsSys.hpp" +#include "AffineSpace.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace Algebra { + +class PolynomialInterface : public multivarFunc{ +public: + //number of variables polynomial expects + virtual size_t numVars() const = 0; + + //evaluates a polynomial on a given assignment + virtual FieldElement eval(const std::vector& x)const = 0; + + //evaluates a polynomial on a given set (represented as vector) of assignments + virtual std::vector evalOnSet(const std::vector>& x_set)const{ + const size_t numEvals = x_set.size(); + std::vector res(numEvals); + for(size_t i=0; i< numEvals; i++){ + res[i] = eval(x_set[i]); + } + + return res; + } + + virtual bool isRoot(const std::vector& x)const {return eval(x) == zero();} + virtual ~PolynomialInterface(){}; + + /** + * @brief Returns an upper bound for degree of this polynomial + * when composed with univariate polynomials (as its variables) + * @param degrees vector of univariate polynomial, name it \f$\vec{d}\f$ + * @return a degree \f$D\f$ such that + * \f$ D \ge \deg P\circ\vec{Q} \f$ when \f$P\f$ is the current polynomial, + * each \f$Q_i\f$ is a univariate polynomial of degree \f$d_i\f$ at most, + * and \f$(P\circ\vec{Q})(x) = P(Q_1(x),Q(2),\dots,Q_n(x))\f$ + */ + virtual PolynomialDegree getDegreeBound(const std::vector& inputDegrees)const = 0; + + /** + * @brief return a clone of the current polynomial + * @return a unique_ptr of PolynomialInterface, + * representing a polynomial equivalent to current + */ + virtual std::unique_ptr clone()const = 0; + + /** + * @brief returns false only if for any assignment \f$ a \f$ + * and for any change of the value of \f$ a_{varId} \f$, + * the result of the evaluation does not change + */ + virtual bool isEffectiveInput(const size_t varId)const{return true;} +}; + +class UnivariatePolynomialInterface : public PolynomialInterface { +public: + size_t numVars() const{ + return 1; + } + + FieldElement eval(const std::vector& x)const { + assert(x.size() == 1); + return eval(x[0]); + } + + PolynomialDegree getDegreeBound(const std::vector& inputDegreesBound)const { + //check if the current polynomial is constant the degree does not change + if(getDegree().isConstantPolyDeg()){ + return getDegree(); + } + assert(inputDegreesBound.size() == 1); + //if the input degree is of a constant polynomial the degree is bounded to 0 + //it is exactly 0 if the p(0) != 0 for p as current polynomial, + //and otherwise it is exactly the degree of the input. + if(inputDegreesBound[0].isConstantPolyDeg()){ + if (getCoefficient(0) != zero()){ + return PolynomialDegree(0); + } + else{ + return inputDegreesBound[0]; + } + } + + //otherwise, both the degrees are non-negative, and the composition degree + //is the product of those degrees + return PolynomialDegree(PolynomialDegree::integral_t(getDegree())*PolynomialDegree::integral_t(inputDegreesBound[0])); + } + + /** + * Return the degree bound for a single input + */ + virtual PolynomialDegree getDegreeBound(const PolynomialDegree inputDeg)const{ + return getDegreeBound(std::vector({inputDeg})); + } + + /** + * @brief Evaluates the polynomial instance using specific element + * @param x the assignment + * @return P(x) + */ + virtual FieldElement eval(const FieldElement& x)const = 0; + + /** + * @brief evaluates a polynomial over a givven space (represented by a ordered basis and affine shift) + * @return the evaluation as a vector, element number \f$ \sum_{i=0}^n b_i \cdot 2^i \f$ represents + * the value at the point \f$ shift + \sum_{i=0}^n (basis element)_i \cdot 2^i \f$ + */ + virtual std::vector eval(const std::vector& orderedBasis, const FieldElement& shift)const{ + + return FFT(getCoefficients(),orderedBasis,shift); + } + + virtual std::vector eval(const AffineSpace& evaluationSpace)const{ + return eval(evaluationSpace.getBasis(), evaluationSpace.getAffineShift()); + } + + /** + * @brief returns the value of the coefficient of \f$x^\text{index}\f$ + * @param index the power of \f$x\f$ the coefficient multiplies + * @return cefficient the value of the coefficient + */ + virtual FieldElement getCoefficient(const unsigned int index)const = 0; + + //returns all coefficients in a vector + virtual const std::vector getCoefficients()const{ + using std::vector; + + //get coefficients + const size_t numCoeffs = std::max(PolynomialDegree::integral_t(0),1+PolynomialDegree::integral_t(getDegree())); + vector coeffs(numCoeffs); + for (size_t i=0; i< numCoeffs; i++){ + coeffs[i] = getCoefficient(i); + } + + return coeffs; + } + + /** + * @brief returns the degree of the polynomial + * @return polynomial degree + */ + virtual PolynomialDegree getDegree()const = 0; +}; + +class DivisorPolynomial : public UnivariatePolynomialInterface { + public: + + /** + * @brief Let this polynomial be \f$a \in \mathbb{F}[x]\f$, and the parameter be \f$b \in \mathbb{F}[x]\f$ + * than by the devision theorem there exists \f$ q,r \in \mathbb{F}[x] \f$ such that \f$ \deg r < \deg a \f$ + * and \f$ b = aq + r \f$. + * This method return \f$q\f$ + */ + virtual std::unique_ptr divideByMe(const UnivariatePolynomialInterface& dividend)const = 0; +}; + + +////an interface with some extra properties used in the cs2BREX reduction. Currently seems needed because of some circular class relations: +//// Polynomial Node holds SelectorSum field, and SelectorSum used to hold CircuitPolynomial vector, before this interface was written +//class TinyRAMPolynomialInterface : public PolynomialInterface{ +// +// virtual std::string asString() const; +// virtual Variable::set getUsedVariables() const; +// virtual void setNewIndices(std::map& old2New); +// +// +//}; + +} //namespace Algebra + +#endif // __POLYNOMIAL_INTERFACE_HPP diff --git a/algebra/algebralib/headers/algebraLib/SelectorSum.hpp b/algebra/algebralib/headers/algebraLib/SelectorSum.hpp new file mode 100644 index 0000000..14e5efa --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/SelectorSum.hpp @@ -0,0 +1,74 @@ +#ifndef ALGEBRA__SELECTOR_SUM_HPP +#define ALGEBRA__SELECTOR_SUM_HPP + +/* SelectorSum.hpp + * The purpose of this class is to have an efficient way of computing the types of polynomials that come up in the TinyRam -> BREX reduction + * In general , this will be sums over i of S_i*P_i where + * S_i - is a `multivariate selector of the binary representation of i'. Specifically, if (b1,..,bm) is the binary rep of i, + * then S_i = (X_1+b1+1)*..*(X_m + bm + 1) . Note that, assuming (X1,..,Xm) are boolean, S_i does not vanish IFF X_1=b_1,..,X_m=b_m + * P_i - a general constraint, but almost always at most a product of linear combinations. + * There is a complication to this description: Typically, there are less distinct P_i's than S_i's. + * So it's worthwhile to store the distinct P_i's, and a map that tells us for each j, what P_i S_j should multiply. + * This is the purpose of the vector selectorToConstraint. This way we compute, e.g., (S_3 + S_5)*P_2 rather than S_3*P_2 + S_5*P_2 - i.e., num of multiplications + * is like num of distinct constraints + */ + +#include +#include +#include +using namespace std; +typedef Algebra::Variable::set varSet; +namespace Algebra{ + class CircuitPolynomial; + class SelectorSum : public PolynomialInterface + { + public: + SelectorSum(); + SelectorSum(const vector& constraints, const vector& selectorVariableVector, const vector& selectorToConstraint, + const vector& selectorRelevant); + + //if selectorRelevant vector not given as input,assume all selectors are relevant + SelectorSum(const vector& constraints, const vector& selectorVariableVector, const vector& selectorToConstraint); + + + + //evaluates a polynomial on a given assignment + FieldElement eval(const vector& x)const; + + //evaluates a polynomial on a given assignment. This one's implemented just to be compatible with old tests - implemented somewhat inefficiently! + FieldElement eval(const VariableAssignment& x)const; + + size_t numVars() const; + + void getConsts(map& FElemMap) const; + string asString() const; + string asStringNewIndex(map& translate, bool isDegreePrint) const; + //the need for this method, is because variable indices are reset in cs2BREX reduction, before passing constraints to BREX + void setNewIndices(std::map& old2New); + + + PolynomialDegree getDegreeBound(const std::vector& inputDegrees)const; + + varSet getUsedVariables()const; + + PolynomialDegree getSelectorDegreeBound(const std::vector& inputDegrees)const; + unique_ptr clone() const; + + private: + + //the need for this method has to do with different selectors having same P_i, as explaing in top of hpp file + vector getConstraintCoeffs(const vector& x)const; + vector getSelectorVals(const vector& x)const; + vector constraints; + vector selectorVars; + varSet selectorVariables;//need to store this in addition to one above is to be able to implememnt the getUsedVariables method - needed for SelectorSum class to + //inside CircuitPolynomial + vector selectorToConstraint; //v[i] tells us what constraint S_i should multiply + vector selectorRelevant; //v[i] tells us if i'th selector S_i really used in this polynomial + //vector selectorVals; + long selectorVarNum, selectorNum, constraintNum; + long selectorNumBound=(long)std::pow(2, selectorVarNum); + }; +} + +#endif // ALGEBRA__SELECTOR_SUM_HPP diff --git a/algebra/algebralib/headers/algebraLib/SubspacePolynomial.hpp b/algebra/algebralib/headers/algebraLib/SubspacePolynomial.hpp new file mode 100644 index 0000000..92459bd --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/SubspacePolynomial.hpp @@ -0,0 +1,24 @@ +/** +* A class for special linearized polynomials - subspace polynomials - that split into distinct roots +The main addition in this clase is a constructor that takes as input a set of elements, +and returns the subspace poly whose roots are exactly the linear span of this set +*/ + +#ifndef SUBSPACE_POLYNOMIAL_HPP__ +#define SUBSPACE_POLYNOMIAL_HPP__ + +#include "LinearizedPolynomial.hpp" +namespace Algebra +{ + + class SubspacePolynomial : public LinearizedPolynomial{ + public: + SubspacePolynomial(const elementsSet_t& spanSet); + + private: + static std::vector computeVanishingPoly(const elementsSet_t& spanSet); + }; +}// namespace Algebra + +#endif // !SUBSPACE_POLYNOMIAL_HPP__ + diff --git a/algebra/algebralib/headers/algebraLib/UnivariatePolynomialGeneral.hpp b/algebra/algebralib/headers/algebraLib/UnivariatePolynomialGeneral.hpp new file mode 100644 index 0000000..d12c410 --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/UnivariatePolynomialGeneral.hpp @@ -0,0 +1,169 @@ +#ifndef __UnivariatePolynomialGeneral_HPP +#define __UnivariatePolynomialGeneral_HPP + +#include "algebraLib/PolynomialInterface.hpp" +#include "algebraLib/AffineSpace.hpp" + +#include +#include + +namespace Algebra{ + +/** + * @class UnivariatePolynomialGeneral + */ +class UnivariatePolynomialGeneral : public DivisorPolynomial { +public: + /** + * @brief Default constructor, generates the zero polynomial + */ + UnivariatePolynomialGeneral(){}; + + /** + * @brief semi copy constructor + */ + UnivariatePolynomialGeneral(const UnivariatePolynomialInterface& src); + + /*sets poly according to coeffs*/ + UnivariatePolynomialGeneral(const std::vector& coeffs); + + /*sets poly according to coeffs, just takes them to itself*/ + UnivariatePolynomialGeneral(std::vector&& coeffs); + /** + * @brief return a clone of the current polynomial + * @return a unique_ptr of PolynomialInterface, + * representing a polynomial equivalent to current + */ + std::unique_ptr clone()const; + + /** + * @brief construct using interpolation over a vector subspace + * (of the Field, as a vector space over GF2) + * + * @param evaluationTabel mapping of the form \f$ x \mapsto y \f$ which the interpulant should agree with + * @param spaceBasis basis of the vector space \f$ V \f$ over GF2 which is a subspace of the current context field, which is the interpolation domain + */ + typedef std::map evaluation_t; + UnivariatePolynomialGeneral(const evaluation_t& evaluationTabel, const elementsSet_t& spaceBasis); + + //interpolation not a vector space + UnivariatePolynomialGeneral(const evaluation_t& evaluationTabel); + + //interpolation over a vector space, represented by a vector of elements + UnivariatePolynomialGeneral(const std::vector& evaluation, const std::vector& orderedBasis, const FieldElement& spaceShift); + + UnivariatePolynomialGeneral(const std::vector& evaluation, const AffineSpace& evaluationSpace); + + /** + * @brief Constructs a constant polynomial + * (a mapping that is independent in the variable) + * @param constant the constant mapping value + */ + explicit UnivariatePolynomialGeneral(const FieldElement& constant); + + /** + * @brief Constructs the monic lowest degree + * polynomial with the given roots + * @param roots set of roots + */ + UnivariatePolynomialGeneral(const elementsSet_t& roots); + + /** + * @brief Evaluates the polynomial instance using specific element + * @param x the assignment + * @return P(x) + */ + FieldElement eval(const FieldElement& x)const; + + /** + * @brief evaluates a polynomial over a givven space (represented by a ordered basis and affine shift) + * @return the evaluation as a vector, element number \f$ \sum_{i=0}^n b_i \cdot 2^i \f$ represents + * the value at the point \f$ shift + \sum_{i=0}^n (basis element)_i \cdot 2^i \f$ + */ + std::vector eval(const std::vector& orderedBasis, const FieldElement& shift)const; + + /** + * @brief Evaluates the polynomial, as a polynomial over \f$ \mathbb{F}[x] \f$ + * @param p input univariate polynomial + * @return The evaluation (composition) \f$this \circ p\f$ such that + * \f$(this \circ p)(x) = this(p(x))\f$ + */ + UnivariatePolynomialInterface* eval(const UnivariatePolynomialInterface& p)const; + + /** + * @brief Changes the value of the coefficient of \f$x^\text{index}\f$ + * @param index the power of \f$x\f$ the coefficient multiplies + * @param cefficient the value of the coefficient + */ + void setCoefficient(const unsigned int index,const FieldElement& coefficient); + + /** + * @brief returns the value of the coefficient of \f$x^\text{index}\f$ + * @param index the power of \f$x\f$ the coefficient multiplies + * @return cefficient the value of the coefficient + */ + FieldElement getCoefficient(const unsigned int index)const; + + //get all coefficients in vector + const std::vector getCoefficients()const; + + /** + * @brief returns the degree of the polynomial + * @return polynomial degree + */ + PolynomialDegree getDegree()const; + + /** + * @brief Let this polynomial be \f$a \in \mathbb{F}[x]\f$, and the parameter be \f$b \in \mathbb{F}[x]\f$ + * than by the devision theorem there exists \f$ q,r \in \mathbb{F}[x] \f$ such that \f$ \deg r < \deg a \f$ + * and \f$ b = aq + r \f$. + * This method return \f$q\f$ + */ + std::unique_ptr divideByMe(const UnivariatePolynomialInterface& dividend)const; + + /** + * @brief Multiply this polynomial by other polynomial + * @param other the other polynomial + */ + void multiply(const UnivariatePolynomialGeneral& other); + + /** + * @brief Multiply this polynomial by a constant + * @param the constant factor + */ + void multiply(const FieldElement& factor); + + /** + * @brief Multiply this polynomial by a monomial + * @param factor the constant factor of the monomial + * @param deg the degree of the monomial + */ + void multiply_by_monomial(const FieldElement& factor, const size_t deg); + + + /** + * @brief Adds this polynomial to other polynomial + * @param other the other polynomial + */ + void add(const UnivariatePolynomialGeneral& other); + + /** Bunch of operators */ + friend UnivariatePolynomialGeneral operator+(const UnivariatePolynomialGeneral&, const UnivariatePolynomialGeneral&); + friend UnivariatePolynomialGeneral& operator+=(UnivariatePolynomialGeneral&, const UnivariatePolynomialGeneral&); + friend UnivariatePolynomialGeneral operator-(const UnivariatePolynomialGeneral&, const UnivariatePolynomialGeneral&); + friend UnivariatePolynomialGeneral operator*(const UnivariatePolynomialGeneral&, const UnivariatePolynomialGeneral&); + friend UnivariatePolynomialGeneral& operator*=(UnivariatePolynomialGeneral&, const UnivariatePolynomialGeneral&); + friend UnivariatePolynomialGeneral operator*(const UnivariatePolynomialGeneral&, const FieldElement&); + friend UnivariatePolynomialGeneral operator*(const FieldElement&, const UnivariatePolynomialGeneral&); + friend UnivariatePolynomialGeneral& operator*=(UnivariatePolynomialGeneral&, const FieldElement&); + friend bool operator==(const UnivariatePolynomialGeneral&, const UnivariatePolynomialGeneral&); + friend bool operator!=(const UnivariatePolynomialGeneral&, const UnivariatePolynomialGeneral&); + friend std::ostream& operator<<(std::ostream&, const UnivariatePolynomialGeneral&); +private: + std::vector polynomial_; /**< @brief instance state */ + +}; + +} // namespace Algebra + +#endif // __UnivariatePolynomialGeneral_HPP diff --git a/algebra/algebralib/headers/algebraLib/novelFFT.hpp b/algebra/algebralib/headers/algebraLib/novelFFT.hpp new file mode 100644 index 0000000..3298160 --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/novelFFT.hpp @@ -0,0 +1,64 @@ +#ifndef _ALGEBRALIB_NOVEL_FFT__HPP +#define _ALGEBRALIB_NOVEL_FFT__HPP + +#include "FieldElement.hpp" +#include "SubspacePolynomial.hpp" +#include + +namespace Algebra{ + +/// +/// This is implementation of the novel FFT +/// introduced in: +/// "Novel Polynomial Basis and Its Application to Reed-Solomon Erasure Codes" +/// By : Sian-Jheng Lin, Wei-Ho Chung, Yunghsiang S. Han +/// +/// This algorithm has O(n \log n ) time complexity, with very low constants +/// It is very easy to use to evaluate many polynomials at one shot over the same space. +/// It's additional space complexity is less then the evaluation size. +/// +/// As this algorithm uses special polynomial basis, +/// it first transforms coefficients over the standard basis to coefficients +/// for the special basis, this is done by evaluation (using Matans FFT) and +/// IFFT using the nofel IFFT algorithm. +/// +/// The coefficients in the special basis are kept for future evaluation. +/// +/// The evaluation function FFT evaluates multiple polynomials at the same time, +/// and writes their result into a matrix. +/// +/// It is optimized for the usage of the project: +/// It performs evaluation only on consecutive blocks of size +/// 2^{orderedBasis.size()} * width +/// Each block it hough as a matrix, where the point (x,y) +/// is at memory address y*width + x. +/// The i'th polynomial evaluation is written on the i'th column. +/// All columns not containing polynomial evaluations set with pad_value. + +class novelFFT{ +public: + + //Initialize with formal sums + novelFFT(const std::vector& orderedBasis, std::vector>&& polysCoeffs, const size_t width, const FieldElement& pad_value); + + //Initialize with evaluations for LDE - implemented for a single poly only because of laziness + novelFFT(const std::vector& orderedBasis, std::vector&& srcEval); + + void FFT(const std::vector& affineShift, FieldElement* dst, const size_t diff_coset)const; +private: + const std::vector X_exp_; + const std::vector polys_; + const std::vector orderedBasis_; + const size_t numPolys_; + const size_t width_; + + //For initialization with formal sums + novelFFT(const std::vector& orderedBasis, const std::vector X_exp, std::vector>&& polysCoeffs, const size_t width, const FieldElement& pad_value); + + //Initialize with evaluations for LDE - implemented for a single poly only because of laziness + novelFFT(const std::vector& orderedBasis, const std::vector X_exp, std::vector&& polyVals); +}; + +} //namespace Algebra + +#endif // _ALGEBRALIB_NOVEL_FFT__HPP diff --git a/algebra/algebralib/headers/algebraLib/variable.hpp b/algebra/algebralib/headers/algebraLib/variable.hpp new file mode 100644 index 0000000..cc91677 --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/variable.hpp @@ -0,0 +1,185 @@ +#ifndef ALGEBRALIB_VARIABLE_HPP_ +#define ALGEBRALIB_VARIABLE_HPP_ + +#include +#include +#include +#include +#include +#include "PolynomialDegree.hpp" +#include "FieldElement.hpp" + +using std::map; + +namespace Algebra{ + +//Forward Declaration +class Variable; +class VariableArray; + +// Typedef +typedef FieldElement FElem; +typedef long VarIndex_t; +typedef ::std::vector VariableArrayContents; +typedef Variable FlagVariable; //Holds variable whos purpose is to be populated with a boolean value, Field(0) or Field(1) + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class Variable ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +/** +Each variable is specified by an index. This can be imagined as the index in x_1, x_2,..., x_i +These are formal variables and do not hold an assignment, later the class VariableAssignment +will give each formal variable its own assignment. +*/ + +class Variable { +private: + static VarIndex_t nextFreeIndex_; + VarIndex_t index_; //This index differentiates and identifies Variable instances. + VarIndex_t newIndex_; // A "renaming" which is done before the reduction to BREX + ::std::string name_; + +public: + explicit Variable(const ::std::string& name = ""); + ::std::string name() const; + ::std::string newIndexName() const; + bool operator==(const Variable& var) const {return this->index_ == var.index_;} + struct VariableStrictOrder { + bool operator()(const Variable& first, const Variable& second)const { + return first.index_ < second.index_; + } + }; + typedef ::std::map VariableAssignment; + FElem eval(const VariableAssignment& assignment) const; + FElem eval(const std::vector& assignment) const; + typedef ::std::set set; + typedef ::std::multiset multiset; + + VarIndex_t getIndex() const { return index_; } + VarIndex_t getNewIndex() const; + void setNewIndex(VarIndex_t newIndex); + + friend class CircuitPolynomial; +}; // class Variable + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ +typedef ::std::map VariableAssignment; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class VariableArray ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class VariableArray : public VariableArrayContents { +private: + ::std::string name_; +public: + explicit VariableArray(const ::std::string& name = ""); + explicit VariableArray(const int size, const ::std::string& name = ""); + explicit VariableArray(const size_t size, const Variable& contents) + : VariableArrayContents(size, contents) {} + + using VariableArrayContents::operator[]; + using VariableArrayContents::at; + using VariableArrayContents::push_back; + using VariableArrayContents::size; + using VariableArrayContents::emplace_back; + + ::std::string name() const; +}; // class VariableArray + +class UnpackedWord : public VariableArray { +public: + UnpackedWord() : VariableArray() {} + UnpackedWord(const size_t numBits, const ::std::string& name) : VariableArray(numBits, name) {} +}; // class UnpackedWord + +typedef ::std::vector UnpackedWordArray; + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class LinearTerm ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class LinearTerm { +private: + Variable variable_; + FElem coeff_; +public: + LinearTerm(const Variable& v) : variable_(v), coeff_(Algebra::one()) {} + LinearTerm(const Variable& v, const FElem& coeff) : variable_(v), coeff_(coeff) {} + LinearTerm& operator*=(const FElem& other) { coeff_ *= other; return *this; } + ::std::string asString() const; + ::std::string asStringNewIndex(map& translate, bool isDegreePrint) const; + FElem eval(const VariableAssignment& assignment) const; + FElem eval(const std::vector& assignment) const; + Variable variable() const { return variable_; } + FElem getCoeff() const { return coeff_; } + void setNewIndices(std::map& old2New); + +}; // class LinearTerm + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class LinearCombination ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class LinearCombination { +protected: + ::std::vector linearTerms_; + FElem constant_; +public: + LinearCombination() : linearTerms_(), constant_(Algebra::zero()) {} + LinearCombination(const Variable& var) : linearTerms_(1, var), constant_(Algebra::zero()) {} + LinearCombination(const LinearTerm& linearTerm); + LinearCombination(const FElem& elem) : linearTerms_(), constant_(elem) {} + + LinearCombination& operator+=(const LinearCombination& other); + LinearCombination& operator*=(const FElem& other); + FElem eval(const VariableAssignment& assignment) const; + FElem eval(const std::vector& assignment) const; + ::std::string asString() const; + ::std::string asStringNewIndex(map& translate, bool isDegreePrint) const; + const Variable::set getUsedVariables() const; + void setNewIndices(std::map& old2New); + PolynomialDegree getDegreeBound(const std::vector& inputDegrees) const; + bool isZero() const { return linearTerms_.size() == 0 && constant_ == Algebra::zero(); } + FElem constant() const { return constant_; } + + friend class CircuitPolynomial; +}; // class LinearCombination + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + +} //namespace Algebra + + + + +#endif //ALGEBRALIB_VARIABLE_HPP_ diff --git a/algebra/algebralib/headers/algebraLib/variable_operators.hpp b/algebra/algebralib/headers/algebraLib/variable_operators.hpp new file mode 100644 index 0000000..c70a82f --- /dev/null +++ b/algebra/algebralib/headers/algebraLib/variable_operators.hpp @@ -0,0 +1,77 @@ +#ifndef ALGEBRALIB_VARIABLE_OPERATORS_HPP_ +#define ALGEBRALIB_VARIABLE_OPERATORS_HPP_ + +#include +#include + +#include + +namespace Algebra { + +// LinearTerm - operator* +inline LinearTerm operator*(const LinearTerm& first, const FElem& second) { auto retval = first; return retval *= second; } +inline LinearTerm operator*(const FElem& first, const LinearTerm& second) { return second * first; } +inline LinearTerm operator*(const Variable& first, const FElem& second) { return LinearTerm(first) * second; } +inline LinearTerm operator*(const FElem& first, const Variable& second) { return second * first; } + +// LinearCombination - operator+ +inline LinearCombination operator+(const LinearCombination& first, const LinearCombination& second) { auto retval = first; return retval += second; } +inline LinearCombination operator+(const LinearCombination& first, const LinearTerm& second) { auto retval = first; return retval += LinearCombination(second); } +inline LinearCombination operator+(const LinearTerm& first, const LinearCombination& second) { return second + first; } +inline LinearCombination operator+(const LinearCombination& first, const Variable& second) { auto retval = first; return retval += LinearCombination(second); } +inline LinearCombination operator+(const Variable& first, const LinearCombination& second) { return second + first; } +inline LinearCombination operator+(const LinearCombination& first, const FElem& second) { auto retval = first; return retval += LinearCombination(second); } +inline LinearCombination operator+(const FElem& first, const LinearCombination& second) { return second + first; } + +inline LinearCombination operator+(const LinearTerm& first, const LinearTerm& second) { return LinearCombination(first) + LinearCombination(second); } +inline LinearCombination operator+(const LinearTerm& first, const Variable& second) { return LinearCombination(first) + LinearCombination(second); } +inline LinearCombination operator+(const Variable& first, const LinearTerm& second) { return second + first; } +inline LinearCombination operator+(const LinearTerm& first, const FElem& second) { return LinearCombination(first) + LinearCombination(second); } +inline LinearCombination operator+(const FElem& first, const LinearTerm& second) { return second + first; } + +inline LinearCombination operator+(const Variable& first, const Variable& second) { return LinearCombination(first) + LinearCombination(second); } +inline LinearCombination operator+(const Variable& first, const FElem& second) { return LinearCombination(first) + LinearCombination(second); } +inline LinearCombination operator+(const FElem& first, const Variable& second) { return second + first; } + +// LinearCombination - operator* +inline LinearCombination operator*(const LinearCombination& first, const FElem& second) { auto retval = first; return retval *= second; } +inline LinearCombination operator*(const FElem& first, const LinearCombination& second) { return second * first; } + +// CircuitPolynomial - operator+ +inline CircuitPolynomial operator+(const CircuitPolynomial& first, const CircuitPolynomial& second) { auto retval = first; return retval += second; } +inline CircuitPolynomial operator+(const CircuitPolynomial& first, const LinearCombination& second) { return first + CircuitPolynomial(second); } +inline CircuitPolynomial operator+(const LinearCombination& first, const CircuitPolynomial& second) { return second + first; } +inline CircuitPolynomial operator+(const CircuitPolynomial& first, const LinearTerm& second) { return first + CircuitPolynomial(second); } +inline CircuitPolynomial operator+(const LinearTerm& first, const CircuitPolynomial& second) { return second + first; } +inline CircuitPolynomial operator+(const CircuitPolynomial& first, const Variable& second) { return first + CircuitPolynomial(second); } +inline CircuitPolynomial operator+(const Variable& first, const CircuitPolynomial& second) { return second + first; } +inline CircuitPolynomial operator+(const CircuitPolynomial& first, const FElem& second) { return first + CircuitPolynomial(second); } +inline CircuitPolynomial operator+(const FElem& first, const CircuitPolynomial& second) { return second + first; } + +// CircuitPolynomial - operator* +inline CircuitPolynomial operator*(const CircuitPolynomial& first, const CircuitPolynomial& second) { auto retval = first; return retval *= second; } +inline CircuitPolynomial operator*(const CircuitPolynomial& first, const LinearCombination& second) { return first * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const LinearCombination& first, const CircuitPolynomial& second) { return second * first; } +inline CircuitPolynomial operator*(const CircuitPolynomial& first, const LinearTerm& second) { return first * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const LinearTerm& first, const CircuitPolynomial& second) { return second * first; } +inline CircuitPolynomial operator*(const CircuitPolynomial& first, const Variable& second) { return first * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const Variable& first, const CircuitPolynomial& second) { return second * first; } +inline CircuitPolynomial operator*(const CircuitPolynomial& first, const FElem& second) { return first * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const FElem& first, const CircuitPolynomial& second) { return second * first; } + +inline CircuitPolynomial operator*(const LinearCombination& first, const LinearCombination& second) { return CircuitPolynomial(first) * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const LinearCombination& first, const LinearTerm& second) { return CircuitPolynomial(first) * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const LinearTerm& first, const LinearCombination& second) { return second * first; } +inline CircuitPolynomial operator*(const LinearCombination& first, const Variable& second) { return CircuitPolynomial(first) * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const Variable& first, const LinearCombination& second) { return second * first; } + + +inline CircuitPolynomial operator*(const LinearTerm& first, const LinearTerm& second) { return CircuitPolynomial(first) * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const LinearTerm& first, const Variable& second) { return CircuitPolynomial(first) * CircuitPolynomial(second); } +inline CircuitPolynomial operator*(const Variable& first, const LinearTerm& second) { return second * first; } + +inline CircuitPolynomial operator*(const Variable& first, const Variable& second) { return CircuitPolynomial(first) * CircuitPolynomial(second); } + +} //namespace + +#endif //ALGEBRALIB_VARIABLE_OPERATORS_HPP \ No newline at end of file diff --git a/algebra/algebralib/src/AffinePolynomial.cpp b/algebra/algebralib/src/AffinePolynomial.cpp new file mode 100644 index 0000000..cdec1bc --- /dev/null +++ b/algebra/algebralib/src/AffinePolynomial.cpp @@ -0,0 +1,160 @@ +/********************************************** AffinePolynomial.cpp ************************************************/ +/** +* @file. +* +* A class of (affine) A polynomials - i.e., +univariate polynomials whose non-zero coefficients_ are only of monomials of the form x^{2^i}, +and possibly, a non-zero constant coefficient. +* +* +*/ +/************************************************************************************************************/ + +#include "algebraLib/AffinePolynomial.hpp" + +#include +#include +#include + +using namespace std; +namespace Algebra { + + /******************************************************************************************************/ + /***************************************** Helper functions *******************************************/ + /******************************************************************************************************/ + + + /* The function evaluates the linear part of the Affine polynomial at a given point and returns the result. + It is used in the computeMat method to evaluate the linear part of the + poly on a basis in order to construct the matrix corresponding + to the poly's operation. + After the affine poly is intialized, this matrix will be used to evaluate it. + Tests showed using the matrix, rather than regular univariate evaluation using coeffs, can make the evaluation + 16 times faster! + */ + FieldElement evalLinearPart(const FieldElement& x, const vector& coefficients) { + + FieldElement res = zero(); + + /*we take advantage of the fact squaring is very efficient in GF2E, + and that x^{2^i} = (x^{2^{i-1})^2. Each iteration we square the current power + of x- power_x, multiply it by the relevant coefficient, and add to the sum, + resulting in sum_{i=0}^{size-1} coeffcieints[i]*x^{2^i} */ + + FieldElement currPower, power_x = x; + for (unsigned long i = 0; i& coefficients, const FieldElement& constantFactor) { + if (coefficients.size()) + { + + // + //Initialize coefficients + // + const FieldElement ZERO = zero(); + + //find highest non zero index: + int lastIndex = coefficients.size() - 1; + while ((lastIndex >= 0) && (coefficients[lastIndex] == ZERO)){ + lastIndex--; + } + coefficients_ = vector(coefficients.begin(), coefficients.begin() + lastIndex + 1); + } + + constantFactor_ = constantFactor; + constantFactor_intRep_ = mapFieldElementToInteger(0,ExtensionDegree,constantFactor); + computeMat(); + } + + unique_ptr AffinePolynomial::clone()const{ + return unique_ptr(new AffinePolynomial(coefficients_, constantFactor_)); + } + + /** The function evaluates the Affine polynomial at a given point and returns the result. */ + FieldElement AffinePolynomial::eval(const Algebra::FieldElement& x)const{ + size_t res = constantFactor_intRep_; + + size_t x_asInt = mapFieldElementToInteger(0,ExtensionDegree,x); + for(int i=0; x_asInt != 0; i++){ + if(x_asInt & 1){ + res ^= polyMat_[i]; + } + x_asInt >>=1; + } + + return mapIntegerToFieldElement(0,ExtensionDegree,res); + } + + //return the i'th coefficient of this polynomial + FieldElement AffinePolynomial::getCoefficient(const unsigned int i)const { + if ( i == 0) return constantFactor_; + + //if "i" is not a power of 2, return zero + if ((1UL< 0){ + return PolynomialDegree(1LL<<(coefficients_.size()-1)); + } + + if (constantFactor_ != ZERO){ + return PolynomialDegree(0); + } + + //otherwise this is the zero polynomial, and its degree is (-infinity) + return PolynomialDegree(-1); + } + + void AffinePolynomial::multiplyByConstant(const FieldElement& factor){ + for(FieldElement& c : coefficients_){ + c *= factor; + } + computeMat(); + } + + + void AffinePolynomial::computeMat() { + const long dim = ExtensionDegree; + + if (coefficients_.size() == 0){ + polyMat_.fill(0); + return; + } + + + for (long i = 0; i < dim; i++){ + const FieldElement x = mapIntegerToFieldElement(i, 1, 1); + polyMat_[i] = mapFieldElementToInteger(0,ExtensionDegree,evalLinearPart(x,coefficients_)); + + } + } + + + + +} // of namespace Algebra diff --git a/algebra/algebralib/src/AffineSpace.cpp b/algebra/algebralib/src/AffineSpace.cpp new file mode 100644 index 0000000..f503788 --- /dev/null +++ b/algebra/algebralib/src/AffineSpace.cpp @@ -0,0 +1,25 @@ +#include "algebraLib/AffineSpace.hpp" + +namespace Algebra{ + +using std::vector; + +AffineSpace::AffineSpace(const orderedBasis_t& basis, const FieldElement& affineShift): + basis_(basis), shift_(affineShift){}; + +const AffineSpace::orderedBasis_t& AffineSpace::getBasis()const{ + return basis_; +} + +const FieldElement& AffineSpace::getAffineShift()const{ + return shift_; +} + +unsigned long long AffineSpace::size()const{ + return 1ULL<<(getBasis().size()); +} + +FieldElement AffineSpace::getElementByIndex(const unsigned long long idx)const{ + return getSpaceElementByIndex(basis_,shift_,idx); +} +} // namespace Algebra diff --git a/algebra/algebralib/src/BitExtract.cpp b/algebra/algebralib/src/BitExtract.cpp new file mode 100644 index 0000000..53e06a5 --- /dev/null +++ b/algebra/algebralib/src/BitExtract.cpp @@ -0,0 +1,187 @@ +/** @file +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#include "algebraLib/BitExtract.hpp" +#include "algebraLib/ErrorHandling.hpp" +#include + +const int DIM=64; +const int matDim = DIM-1; + +namespace Algebra { + + const FieldElement invExtrType::invExtrArr[invExtrArrSize] = { + mapIntegerToFieldElement(0, DIM, 1 + 2 + 4 + 8), //-1=xor @0 + mapIntegerToFieldElement(0, DIM, 8 + 2), //0 @1 + mapIntegerToFieldElement(0, DIM, 8 + 4), //1 @2 + mapIntegerToFieldElement(0, DIM, 16 + 8), //2 @3 + mapIntegerToFieldElement(0, DIM, 0x5CB972E5CB972E52), //3 @4 + mapIntegerToFieldElement(0, DIM, 0xDB6DB6DB6DB6DB6A), //4 @5 + mapIntegerToFieldElement(0, DIM, 0xB6DB6DB6DB6DB6CF), //5 @6 + mapIntegerToFieldElement(0, DIM, 0x6DB6DB6DB6DB6D85), //6 @7 + mapIntegerToFieldElement(0, DIM, 0xDB6DB6DB6DB6DB0A), //7 @8 + mapIntegerToFieldElement(0, DIM, 0xB6DB6DB6DB6DB00F), //11 @9 + mapIntegerToFieldElement(0, DIM, 0xDB6DB6DB6DB6C00A), //13 @10 + mapIntegerToFieldElement(0, DIM, 0x6DB6DB6DB6DB0005), //15 @11 + mapIntegerToFieldElement(0, DIM, 0x6DB6DB6DB6D80005), //18 @12 + mapIntegerToFieldElement(0, DIM, 0xDB6DB6DB6D80000A), //22 @13 + mapIntegerToFieldElement(0, DIM, 0xDB6DB6DB6C00000A), //25 @14 + mapIntegerToFieldElement(0, DIM, 0xB6DB6DB60000000F), //32 @15 + mapIntegerToFieldElement(0, DIM, 0xB6DB6D800000000F), //38 @16 + mapIntegerToFieldElement(0, DIM, 0x6DB6D80000000005), //42 @17 + mapIntegerToFieldElement(0, DIM, 0xDB6DB0000000000A), //43 @18 + mapIntegerToFieldElement(0, DIM, 0xDB6C00000000000A), //49 @19 + mapIntegerToFieldElement(0, DIM, 0x6DB0000000000005), //51 @20 + mapIntegerToFieldElement(0, DIM, 0x6C00000000000005), //57 @21 + mapIntegerToFieldElement(0, DIM, 0xB6DB6DB6DB6DB60F),//8 @22 + mapIntegerToFieldElement(0, DIM, 0x6DB6DB6DB6DB6C05),//9 @23 + mapIntegerToFieldElement(0, DIM, 0xDB6DB6DB6DB6D80A),//10 @24 + mapIntegerToFieldElement(0, DIM, 0x6DB6DB6DB6DB6005),//12 @25 + mapIntegerToFieldElement(0, DIM, 0xB6DB6DB6DB6D800F)//14 @26 + + }; + /*inline*/ const FieldElement& invExtrType::operator[](const int i)const{ + switch (i) { + case -1: return invExtrArr[0]; + case 0: return invExtrArr[1]; + case 1: return invExtrArr[2]; + case 2: return invExtrArr[3]; + case 3: return invExtrArr[4]; + case 4: return invExtrArr[5]; + case 5: return invExtrArr[6]; + case 6: return invExtrArr[7]; + case 7: return invExtrArr[8]; + case 11: return invExtrArr[9]; + case 13: return invExtrArr[10]; + case 15: return invExtrArr[11]; + case 18: return invExtrArr[12]; + case 22: return invExtrArr[13]; + case 25: return invExtrArr[14]; + case 32: return invExtrArr[15]; + case 38: return invExtrArr[16]; + case 42: return invExtrArr[17]; + case 43: return invExtrArr[18]; + case 49: return invExtrArr[19]; + case 51: return invExtrArr[20]; + case 57: return invExtrArr[21]; + case 8: return invExtrArr[22]; + case 9: return invExtrArr[23]; + case 10: return invExtrArr[24]; + case 12: return invExtrArr[25]; + case 14: return invExtrArr[26]; + + default: throw; + } + } + + const invExtrType invExtrConsts; + + /* + * Boolean matrix inverse + * Using Gaussian elimination + */ + std::array getInv(const std::array& src){ + std::array srcMat(src); + std::array unitMat; + for(int i=0; i< matDim; i++){ + unitMat[i] = 1UL< matForBitExtr(const int bitNum) { + std::array M; + M.fill(0UL); + const FieldElement invExtractBit = invExtrConsts[bitNum]; + const FieldElement x = xFE(); + FieldElement y = x; //not one; + + for (int i = 0; i < matDim; ++i) { + FieldElement tmp = invExtractBit*(y*y + y); + size_t v = mapFieldElementToInteger(0,ExtensionDegree,tmp); + for (int j = 0; j < DIM - 1; ++j){ + if (j < bitNum){ + M[i] |= (v & 1)<>1)<>=1; + } + y *= x; + } + + return getInv(M); + } + + //TODO: half-trace method ii.2.4 + FieldElement extractBit(const FieldElement& elem, const int bitNum, const std::array& invM) { + static const FieldElement x = xFE(); + const size_t tvec = mapFieldElementToInteger(0,ExtensionDegree,elem); + size_t vcoeffs = 0; + for (int j = 0; j < DIM - 1; ++j){ + const size_t entryMask = 1UL<>1) & entryMask; + } + } + + size_t v = 0; + // v= vcoeffs * invM + for(int i=0; i< matDim; i++){ + if((vcoeffs>>i) & 1UL){ + v ^= invM[i]; + } + } + FieldElement y = zero(); + FieldElement tmp = x; //1coeff dontcare + for (int j = 0; j < DIM - 1; ++j) { + if ((v & (1UL< +#include +#include + + +namespace Algebra{ + +CircuitPolynomial::CircuitPolynomial(const vector& factors) : + operation_(PolynomialOperation::PROD), input_(), factors_(factors){ + ALGEBRALIB_ASSERT(factors_.size() > 0, "CircuitPolynomial c'tor: Need at least one factor in product"); +} + +CircuitPolynomial::CircuitPolynomial(const SelectorSum& S) : + operation_(PolynomialOperation::SELECTORSUM), S_(new SelectorSum(S)), input_(){} + +CircuitPolynomial::CircuitPolynomial(const LinearTerm& linearTerm) : + operation_(PolynomialOperation::PROD), input_(), factors_() { + if (linearTerm.getCoeff() == Algebra::zero()) { + factors_.emplace_back(LinearCombination(Algebra::zero())); + } + else { + factors_.emplace_back(linearTerm); + } +} + + +bool CircuitPolynomial::isZero() const { + return operation_ == PolynomialOperation::PROD && + input_.size() == 0 && + factors_.size() == 1 && factors_[0].isZero(); +} + +CircuitPolynomial& CircuitPolynomial::operator+=(const CircuitPolynomial& other) { + if (other.isZero()) { + return *this; + } + if (isZero()) { + *this = other; + return *this; + } + if (operation_ == PolynomialOperation::ADD) { + input_.emplace_back(other); + return *this; + } + if (operation_ == PolynomialOperation::PROD && + other.operation_ == PolynomialOperation::PROD && + factors_.size() == 1 && other.factors_.size() == 1) { + ALGEBRALIB_ASSERT(input_.size() == 0 && other.input_.size() == 0, + "CircuitPolynomial operator += : When the operation is PROD there should be no inputs "); + factors_[0] += other.factors_[0]; + return *this; + } + CircuitPolynomial input1(*this); + operation_ = PolynomialOperation::ADD; + input_.clear(); + factors_.clear(); + input_.emplace_back(input1); + input_.emplace_back(other); + return *this; +} + +CircuitPolynomial& CircuitPolynomial::operator*=(const CircuitPolynomial& other) { + if (other.isZero() || isZero()) { + input_.clear(); + factors_.clear(); + operation_ = PolynomialOperation::PROD; + factors_.emplace_back(LinearCombination(Algebra::zero())); + return *this; + } + if (operation_ == PolynomialOperation::PROD && operation_ == other.operation_) { + for (const LinearCombination& l : other.factors_) { + factors_.emplace_back(l); + } + return *this; + } + if (operation_ == PolynomialOperation::MUL) { + input_.emplace_back(other); + return *this; + } + CircuitPolynomial input1(*this); + operation_ = PolynomialOperation::MUL; + input_.clear(); + factors_.clear(); + input_.emplace_back(input1); + input_.emplace_back(other); + return *this; +} + +std::string CircuitPolynomial::asString() const { + if (operation_ == PolynomialOperation::SELECTORSUM) { + return "(" + S_->asString() + ")"; + } + std::string resString = ""; + if (operation_ == PolynomialOperation::PROD){ + ALGEBRALIB_ASSERT(factors_.size() > 0, "CircuitPolynomial asString - factors must have at least one LinearCombination"); + resString = factors_[0].asString(); + for (unsigned int i = 1; i < factors_.size(); i++){ + resString += "*" + factors_[i].asString(); + } + return resString; + } + resString += "("; + int inputSize = input_.size() - 1; + for (auto& polynomial : input_){ + resString += polynomial.asString(); + if (inputSize != 0){ + switch (operation_) { + case(PolynomialOperation::ADD) : + resString += "+"; + break; + case(PolynomialOperation::MUL) : + resString += "*"; + break; + default: + ALGEBRALIB_FATAL("asString: Unknown polynomial operation"); + break; + } + } + --inputSize; + } + resString += ")"; + return resString; +} + +string CircuitPolynomial::asStringNewIndex(map& translate, bool isDegreePrint) const { + if (operation_ == PolynomialOperation::SELECTORSUM) { + return "(" + S_->asStringNewIndex(translate, isDegreePrint) + ")"; + } + std::string resString = ""; + if (operation_ == PolynomialOperation::PROD){ + ALGEBRALIB_ASSERT(factors_.size() > 0, "CircuitPolynomial asStringNewIndex - factors must have at least one LinearCombination"); + resString = factors_[0].asStringNewIndex(translate, isDegreePrint); + for (unsigned int i = 1; i < factors_.size(); i++){ + resString += "*" + factors_[i].asStringNewIndex(translate, isDegreePrint); + } + return resString; + } + resString += "("; + int inputSize = input_.size() - 1; + for (auto& polynomial : input_){ + resString += polynomial.asStringNewIndex(translate, isDegreePrint); + if (inputSize != 0){ + switch (operation_) { + case(PolynomialOperation::ADD) : + resString += "+"; + break; + case(PolynomialOperation::MUL) : + resString += "*"; + break; + default: + ALGEBRALIB_FATAL("asString: Unknown polynomial operation"); + break; + } + } + --inputSize; + } + resString += ")"; + return resString; +} + +void CircuitPolynomial::getConsts(map& FElemMap) const { + if (operation_ == PolynomialOperation::SELECTORSUM) { + S_->getConsts(FElemMap); + } + if (operation_ == PolynomialOperation::PROD) { + for (unsigned int i = 0; i < factors_.size(); i++) { + FElem lconst = factors_[i].constant(); + if (FElemMap.find(lconst) == FElemMap.end()) { + FElemMap[lconst] = mapFieldElementToInteger(0,64,lconst); + } + for (unsigned int j = 0; j < factors_[i].linearTerms_.size(); j++) { + FElem coeff = factors_[i].linearTerms_[j].getCoeff(); + if (FElemMap.find(coeff) == FElemMap.end()) { + FElemMap[coeff] = mapFieldElementToInteger(0, 64, coeff); + } + } + } + return; + } + if (operation_ == PolynomialOperation::MUL || operation_ == PolynomialOperation::ADD) { + for (auto& polynomial : input_) { + polynomial.getConsts(FElemMap); + } + return; + } +} + + +//#define noSelector // flag to check how much time is taken by selectors +FElem CircuitPolynomial::eval(const std::vector& assignment) const{ + if (operation_ == PolynomialOperation::SELECTORSUM) { + return(S_->eval(assignment)); + } + { + FElem retVal = Algebra::one(); + if (operation_ == PolynomialOperation::PROD){ +#ifndef noSelector//just added to measure how much time spent here + for (const auto& factor : factors_) + retVal *= factor.eval(assignment); +#endif + return retVal; + } + } + bool first = true; + FElem retval; + for (const auto& polynomial : input_){ + if (first){ + retval = polynomial.eval(assignment); + first = false; + continue; + } + switch (operation_) + { + case(PolynomialOperation::ADD) : + retval += polynomial.eval(assignment); + break; + case(PolynomialOperation::MUL) : + retval *= polynomial.eval(assignment); + break; + default: + ALGEBRALIB_FATAL("DFSEval: Unknown polynomial operation"); + break; + } + } + return retval; +} + + +FElem CircuitPolynomial::eval(const VariableAssignment& assignment) const{ + if (operation_ == PolynomialOperation::SELECTORSUM) { + return S_->eval(assignment); + } + { + FElem retVal = Algebra::one(); + if (operation_ == PolynomialOperation::PROD){ +#ifndef noSelector//just added to measure how much time spent here + for (const auto& factor : factors_) + retVal *= factor.eval(assignment); +#endif + return retVal; + } + } + bool first = true; + FElem retval; + for (const auto& polynomial : input_){ + if (first){ + retval = polynomial.eval(assignment); + first = false; + continue; + } + switch (operation_) { + case(PolynomialOperation::ADD) : + retval += polynomial.eval(assignment); + break; + case(PolynomialOperation::MUL) : + retval *= polynomial.eval(assignment); + break; + default: + ALGEBRALIB_FATAL("DFSEval: Unknown polynomial operation"); + break; + } + } + return retval; +} + +size_t CircuitPolynomial::numVars() const{ + ALGEBRALIB_FATAL("Not implemented"); +} + +bool CircuitPolynomial::isSatisfied(const VariableAssignment& assignment) const{ + FElem val = eval(assignment); + return (val == Algebra::zero()); +} + + +Variable::set CircuitPolynomial::getUsedVariables() const{ + if (operation_ == PolynomialOperation::SELECTORSUM) + return S_->getUsedVariables(); + Variable::set retSet; + if (operation_ == PolynomialOperation::PROD) { + for (const auto& linearComb : factors_){ + Variable::set polySet = linearComb.getUsedVariables(); + retSet.insert(polySet.begin(), polySet.end()); + } + } + else{ + for (const auto& poly : input_) { + Variable::set polySet = poly.getUsedVariables(); + retSet.insert(polySet.begin(), polySet.end()); + } + } + return retSet; +} + +void CircuitPolynomial::setNewIndices(std::map& old2New){ + if (operation_ == PolynomialOperation::SELECTORSUM) + S_->setNewIndices(old2New); + + if (operation_ == PolynomialOperation::PROD){ + for (auto& factor : factors_) + factor.setNewIndices(old2New); + } + for (auto& polynomial : input_){ + polynomial.setNewIndices(old2New); + } +} + + +PolynomialDegree CircuitPolynomial::getDegreeBound(const std::vector& inputDegrees) const{ + if (operation_ == PolynomialOperation::SELECTORSUM) { + return S_->getDegreeBound(inputDegrees); + } + + PolynomialDegree maxDegree(0); + + if (operation_ == PolynomialOperation::PROD){ + for (const auto& factor : factors_){ + maxDegree = degreeOfProduct(maxDegree, factor.getDegreeBound(inputDegrees)); + } + return maxDegree; + } + for (auto& poly : input_){ + PolynomialDegree polyDegree = poly.getDegreeBound(inputDegrees); + switch (operation_) { + case(PolynomialOperation::ADD) : + maxDegree = std::max(maxDegree, polyDegree); + break; + case(PolynomialOperation::MUL) : + maxDegree = degreeOfProduct(maxDegree, polyDegree); + break; + default: + ALGEBRALIB_FATAL("getDegeeBound: Unknown polynomial operation"); + break; + } + } + return maxDegree; +} + +bool CircuitPolynomial::isEffectiveInput(const size_t varId) const { + //return true; + Variable::set usedVariables = this->getUsedVariables(); + for (const Variable& v : usedVariables){ + int id = v.getNewIndex(); + ALGEBRALIB_ASSERT(id >= 0, "new ID should be initialized and positive"); + if (varId == (unsigned int)id){ + return true; + } + } + return false; +} +} diff --git a/algebra/algebralib/src/ErrorHandling.cpp b/algebra/algebralib/src/ErrorHandling.cpp new file mode 100644 index 0000000..de58513 --- /dev/null +++ b/algebra/algebralib/src/ErrorHandling.cpp @@ -0,0 +1,97 @@ +/** @file +***************************************************************************** +Common functionality needed by many components. +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "algebraLib/ErrorHandling.hpp" + + +#ifdef __linux__ +#include +#include +#endif +#ifdef __GLIBC__ +#include // backtraces +#endif + +namespace Algebra{ + + /********************************************************/ + /*************** Debug String Formatting ****************/ + /********************************************************/ + +#ifdef DEBUG + const static size_t MAX_FMT = 256; + ::std::string ALGEBRALIB_FMT(const char* format, ...) { + char buf[MAX_FMT]; + va_list args; + va_start(args, format); +#ifdef __linux__ + const int strChk = snprintf(buf, MAX_FMT, format, args); +#else // #ifdef __linux__ + const int strChk = vsnprintf_s(buf, MAX_FMT, MAX_FMT, format, args); +#endif // #ifdef __linux__ + va_end(args); + ALGEBRALIB_ASSERT(strChk >= 0 && (unsigned int)strChk < MAX_FMT, "String length larger than buffer. Shorten" + " string or increase buffer size defined in \"MAX_FMT\"."); + return ::std::string(buf); + } +#else // not DEBUG + ::std::string ALGEBRALIB_FMT(const char* format, ...) { return ""; } +#endif + + + /*****************************************************************************/ + /*********************** ErrorHandling********** ****************************/ + /*****************************************************************************/ + + /* + TODO add dumping of environment variables and run command to a log file and add log file path + to release mode error message. We don't want people running release version to get any internal + information (variable values, stack trace, etc.) but want to have every data possible to + reproduce assertion. + */ + void ErrorHandling::fatalError(const ::std::string& msg) { +# ifdef DEBUG + ::std::cerr << "ERROR: " << msg << ::std::endl << ::std::endl; + printStacktrace(); + throw ::std::runtime_error(msg); +# else // not DEBUG + const ::std::string releaseMsg("Fatal error encoutered. Run debug build for more" + " information and stack trace."); + ::std::cerr << "ERROR: " << releaseMsg << ::std::endl << ::std::endl; + throw ::std::runtime_error(releaseMsg); +# endif + } + + void ErrorHandling::fatalError(const ::std::stringstream& msg) { + fatalError(msg.str()); + } + + void ErrorHandling::printStacktrace() { +#ifdef __GNUC__ + std::cerr << "Stack trace (pipe through c++filt to demangle identifiers):" << std::endl; + const int maxFrames = 100; + void* frames[maxFrames]; + // Fill array with pointers to stack frames + int numFrames = backtrace(frames, maxFrames); + // Decode frames and print them to stderr + backtrace_symbols_fd(frames, numFrames, STDERR_FILENO); +#else + //TODO make this available for Windows + std::cerr << " (stack trace not available on this platform)" << std::endl; +#endif // __GNUC__ + } + +} // namespace Algebra + diff --git a/algebra/algebralib/src/FFT.cpp b/algebra/algebralib/src/FFT.cpp new file mode 100644 index 0000000..394aded --- /dev/null +++ b/algebra/algebralib/src/FFT.cpp @@ -0,0 +1,120 @@ +#include "algebraLib/FFT.hpp" +#include "algebraLib/ErrorHandling.hpp" +#include "FFT.h" +#include +#include + +/// Important : all this assumes Matans irredusible is used for the context field + +namespace Algebra{ +using std::vector; +using std::max; + +vector IFFT(const vector& polyEvaluation, const vector& orderedBasis, const FieldElement& shift){ + + ALGEBRALIB_ASSERT(polyEvaluation.size() == (1UL< vals_vec(polyEvaluation); + FieldElement* values = (FieldElement*)(&(vals_vec[0])); + IFFT_inplace(values,orderedBasis,shift); + return vals_vec; +} + +void IFFT_inplace(FieldElement* polyEvaluation, const vector& orderedBasis, const FieldElement& shift){ + + FFF::Element* basis_vec = (FFF::Element*)(&(orderedBasis[0])); + FFF::Element shift_fff(shift); + + FFF::Basis basis(basis_vec, orderedBasis.size(),shift_fff); + + FFF::FFT fftInstance(basis,FFF::IFFT_OP); + fftInstance.AlgIFFT((FFF::Element*)polyEvaluation); +} + +namespace{ + //assumes x > 0 + short correct_ceilLog2(const size_t x){ + //just for disabling an infinite loop + if (x == 0){ + return 0; + } + short log=0; + size_t y = x; + while (y != 1){ + log++; + y >>= 1; + } + + if (x > (1UL< FFT(const vector& poly, const vector& orderedBasis, const FieldElement& shift){ + + const size_t evaluationSapceSize = (1L< vals_vec(poly); + //enlarge if needed to hold result + vals_vec.resize(evaluationSapceSize,zero()); + + FieldElement* values = (FieldElement*)(&(vals_vec[0])); + FFT_inplace(values,orderedBasis,shift,max(int(correct_ceilLog2(poly.size())),1)); + + return vals_vec; +} + +void FFT_inplace(FieldElement* poly, const vector& orderedBasis, const FieldElement& shift, const size_t polyDeg_ceilLog){ + const size_t evaluationSapceSize = (1L< shiftsBasis(orderedBasis.begin()+polyDeg_ceilLog,orderedBasis.end()); + const size_t numShifts = 1L< LDE(const vector& polyEvaluation, const vector& orderedBasis_src ,const FieldElement& shift_src, const vector& orderedBasis_dst, const FieldElement& shift_dst){ + ALGEBRALIB_ASSERT(orderedBasis_src.size() <= orderedBasis_dst.size(), "LDE is supported only for destination evaluation spaces of size at least as the source evaluation space"); + ALGEBRALIB_ASSERT(polyEvaluation.size() == (1UL< vals_vec(polyEvaluation); + //enlarge if needed to hold result + if(vals_vec.size() < evaluationSapceSize){ + vals_vec.resize(evaluationSapceSize,zero()); + } + + FieldElement* values = (FieldElement*)(&(vals_vec[0])); + LDE_inplace(values, orderedBasis_src,shift_src,orderedBasis_dst,shift_dst); + + return vals_vec; +} + +void LDE_inplace(FieldElement* polyEvaluation, const vector& orderedBasis_src ,const FieldElement& shift_src, const vector& orderedBasis_dst, const FieldElement& shift_dst){ + + IFFT_inplace(polyEvaluation,orderedBasis_src,shift_src); + FFT_inplace(polyEvaluation,orderedBasis_dst,shift_dst,orderedBasis_src.size()); +} +} //namespace Algebra diff --git a/algebra/algebralib/src/FieldElement.cpp b/algebra/algebralib/src/FieldElement.cpp new file mode 100644 index 0000000..9534a50 --- /dev/null +++ b/algebra/algebralib/src/FieldElement.cpp @@ -0,0 +1,206 @@ +#include "algebraLib/FieldElement.hpp" +#include "algebraLib/ErrorHandling.hpp" +#include "algebraLib/SubspacePolynomial.hpp" +#include +#include +#include +#include +#include + +namespace Algebra { + using std::vector; + std::mt19937_64 rng; + + + std::ostream& operator<<(std::ostream& s, const FieldElement& a) + { + return s << a.asString(); + } + + std::string FieldElement::asString() const{ + ::std::stringstream s; + s << "["; + size_t elem=element_.c[0]; + bool first = true; + while(elem != 0){ + if(!first){ + s<<" "; + } + s<<(elem & 1); + elem>>=1; + + first = false; + } + s <<"]"; + return s.str(); + } + + +elementsSet_t getStandartBasis(const unsigned short basisSize){ + elementsSet_t basis; + for(unsigned short i=0; i< basisSize; i++){ + basis.insert(mapIntegerToFieldElement(i,1,1)); + } + return basis; +} + +vector getStandartOrderedBasis(const unsigned short basisSize){ + vector basis; + for(unsigned short i=0; i< basisSize; i++){ + basis.push_back(mapIntegerToFieldElement(i,1,1)); + } + return basis; +} + + +FieldElement generateRandom(){ + //return mapIntegerToFieldElement(0,64,rand()); + //return mapIntegerToFieldElement(0, 64, ((unsigned long long)rand() << 32) + rand()); + return mapIntegerToFieldElement(0,64,rng()); +} + +FieldElement getSpaceElementByIndex(const std::vector& orderedBasis, const FieldElement& affineShift, const size_t index){ + size_t iter = index; + FieldElement res = zero(); + + for(const auto& b : orderedBasis){ + if(iter % 2 == 1) res += b; + iter >>= 1; + } + + return affineShift + res; +} + +size_t getSpaceIndexOfElement(const std::vector& orderedBasis, const FieldElement& affineShift, const FieldElement& e){ + FieldElement elemInSpan = e - affineShift; + + vector partialBasis = orderedBasis; + const auto firstElemLoc = orderedBasis.begin(); + auto lastElemLoc = orderedBasis.end(); + + //first check "e" really is in space + { + SubspacePolynomial spacePoly(elementsSet_t(firstElemLoc,lastElemLoc)); + if (spacePoly.eval(elemInSpan) != zero()){ + ALGEBRALIB_FATAL("element is not in spanned space"); + } + } + + //now check for every basis element if + //elemInSpace is in the space spanned by the compliment basis set + //(using the space polynomial) + //if so, the coefficient of it in the representation is zero + //otherwise is one + + size_t elementIndex = 0; + lastElemLoc--; + for(;firstElemLoc <= lastElemLoc; lastElemLoc--){ + elementIndex<<=1; + + SubspacePolynomial spacePoly(elementsSet_t(firstElemLoc,lastElemLoc)); + if (spacePoly.eval(elemInSpan) != zero()){ + //add to index + elementIndex+=1; + + //take the element projection on the subspace + elemInSpan -= *lastElemLoc; + } + + if (firstElemLoc == lastElemLoc){ + break; + } + } + + return elementIndex; +} + +vector invertPointwise(const vector& elems){ + + if(elems.size() == 0){ + return elems; + } + + std::stack layersEnd; + + vector treeOfElems(elems); + layersEnd.push(treeOfElems.size()); + size_t currLocation = 0; + + + //this loop builds a binary tree + //the leafs hold the elements to invert + //and each inner node holds the product of both its sons. + //In the case the number of elements in some depth is odd, + //one of the elements (the last one) is a single child of its + //father in the higher layer + while(true){ + const size_t currLayerEnd = layersEnd.top(); + while(currLocation < currLayerEnd){ + + //handling the case of a single child + //because current layer contains odd number of nodes + if(currLocation +1 == currLayerEnd){ + treeOfElems.push_back(treeOfElems[currLocation]); + currLocation+=1; + } + + //handles the "ordinary case" + //where two brothers are multiplied to get their fathers + //value + else{ + treeOfElems.push_back(treeOfElems[currLocation]*treeOfElems[currLocation+1]); + currLocation+=2; + } + } + + //end when the next layer contains a single element + //it is the root + if(currLayerEnd+1 == treeOfElems.size()){ + break; + } + + layersEnd.push(treeOfElems.size()); + } + + //invert the root (the product of all elements + treeOfElems[treeOfElems.size()-1] = treeOfElems[treeOfElems.size()-1].inverse(); + + //calculated the inverse of each subtree node + while(!layersEnd.empty()){ + const size_t currLayerEnd = layersEnd.top(); + layersEnd.pop(); + size_t currLayerStart = 0; + if(!layersEnd.empty()){ + currLayerStart = layersEnd.top(); + } + + size_t parentIndex = currLayerEnd; + size_t childIndex = currLayerStart; + while (childIndex < currLayerEnd){ + //handling the case of a single child + //because current layer contains odd number of nodes + if(childIndex +1 == currLayerEnd){ + treeOfElems[childIndex] =treeOfElems[parentIndex]; + childIndex+=1; + //no need to advance parentIndex because this is the last child in the layer + } + + //handles the "ordinary case" + //where each of the two children's inverse is the product of the parent and the brother + else{ + const FieldElement thisChildVal = treeOfElems[parentIndex]*treeOfElems[childIndex+1]; + const FieldElement brothersVal = treeOfElems[parentIndex]*treeOfElems[childIndex]; + treeOfElems[childIndex] = thisChildVal; + treeOfElems[childIndex+1] = brothersVal; + childIndex+=2; + parentIndex+=1; + } + } + } + + treeOfElems.resize(elems.size()); + return treeOfElems; +} + + +}// namespace Algebra diff --git a/algebra/algebralib/src/LinearPolynomial.cpp b/algebra/algebralib/src/LinearPolynomial.cpp new file mode 100644 index 0000000..0c8cc8c --- /dev/null +++ b/algebra/algebralib/src/LinearPolynomial.cpp @@ -0,0 +1,37 @@ +#include "algebraLib/LinearPolynomial.hpp" + +namespace Algebra{ + +using std::unique_ptr; + +unique_ptr LinearPolynomial::clone()const{ + return unique_ptr(new LinearPolynomial(*this)); +} + +FieldElement LinearPolynomial::getCoefficient(const unsigned int index)const{ + switch (index){ + case 0 : return coefficient0_; + case 1 : return coefficient1_; + default : return zero(); + } +} + +PolynomialDegree LinearPolynomial::getDegree()const{ + if(coefficient1_ != zero()) return PolynomialDegree(1); + if(coefficient0_ != zero()) return PolynomialDegree(0); + return PolynomialDegree(-1); +} + +LinearPolynomial LinearPolynomial::compose(const LinearPolynomial& p)const{ + return LinearPolynomial(coefficient0_+coefficient1_*p.coefficient0_, coefficient1_*p.coefficient1_); +} + +bool operator==(const LinearPolynomial& p1, const LinearPolynomial& p2){ + return (p1.getCoefficient(0) == p2.getCoefficient(0)) && (p1.getCoefficient(1) == p2.getCoefficient(1)); +} + +LinearPolynomial operator+(const LinearPolynomial& p, const FieldElement& c){ + return LinearPolynomial(p.getCoefficient(0) + c, p.getCoefficient(1)); +} + +} // namespace Algebra diff --git a/algebra/algebralib/src/MappingsSys.cpp b/algebra/algebralib/src/MappingsSys.cpp new file mode 100644 index 0000000..d292199 --- /dev/null +++ b/algebra/algebralib/src/MappingsSys.cpp @@ -0,0 +1,40 @@ +#include "algebraLib/MappingsSys.hpp" +#include "algebraLib/ErrorHandling.hpp" + +namespace Algebra{ + +using std::vector; +using std::unique_ptr; + +namespace{ + class linearCombFunc : public multivarFunc{ + public: + linearCombFunc(const MappingsSys& mappings, const vector& coeffs): + mappings_(mappings.clone()), coeffs_(coeffs){}; + + size_t numVars() const{ + return mappings_->numVars(); + } + + FieldElement eval(const vector& assignment) const{ + const vector multiRes = mappings_->eval(assignment); + FieldElement res = Algebra::zero(); + for(unsigned int i=0; i mappings_; + const vector coeffs_; + }; +} + +multivarFunc* MappingsSys::getLinearComb(const vector& coeffs) const{ + ALGEBRALIB_ASSERT(coeffs.size() == numMappings(), "number of coefficients is expected to be same as number of mappings"); + return new linearCombFunc(*this,coeffs); +} + +} // namespace Algebra diff --git a/algebra/algebralib/src/PolynomialDegree.cpp b/algebra/algebralib/src/PolynomialDegree.cpp new file mode 100644 index 0000000..e47b669 --- /dev/null +++ b/algebra/algebralib/src/PolynomialDegree.cpp @@ -0,0 +1,63 @@ +#include "algebraLib/PolynomialDegree.hpp" +#include +#include + +using std::max; + +namespace Algebra { + +PolynomialDegree::PolynomialDegree(const integral_t degree):degree_(degree) { + if (degree_ < 0) degree_ = -1; +}; + +bool PolynomialDegree::isInteger()const{ + return degree_ >= 0; +} + +PolynomialDegree::operator integral_t()const { + return degree_; +} + +PolynomialDegree degreeOfProduct(const PolynomialDegree& d1, const PolynomialDegree& d2){ + //if at least one of the degrees is -infinity + //return -infinity + if(!d1.isInteger() || !d2.isInteger())return PolynomialDegree::getZeroPolyDegree(); + + //else, both degrees are integers, and the degree of the product + //is their sum + return PolynomialDegree(PolynomialDegree::integral_t(d1) + PolynomialDegree::integral_t(d2)); +} + +//Return the degree bound of polyA(polyB) +PolynomialDegree degreeOfComposition(const PolynomialDegree& polyA_deg, const PolynomialDegree& polyB_deg){ + //if A is the Zero polynomial (it has -infinity degree) + //return -infinity + if (!polyA_deg.isInteger()) return PolynomialDegree::getZeroPolyDegree(); + + //else if B is a constant polynomial (of degree at most 0) + //the composition is constant as well, so return 0 + if (PolynomialDegree::integral_t(polyB_deg) <= 0) return PolynomialDegree(0); + + //else, both degrees are non-negative, the degree of the composition + //is their product + return PolynomialDegree(PolynomialDegree::integral_t(polyA_deg)*PolynomialDegree::integral_t(polyB_deg)); +} + +bool operator<(const PolynomialDegree& d1, const PolynomialDegree& d2){ + return PolynomialDegree::integral_t(d1) < PolynomialDegree::integral_t(d2); +} + +bool operator==(const PolynomialDegree& d1, const PolynomialDegree& d2){ + if(d1.isInteger() && d2.isInteger()){ + return PolynomialDegree::integral_t(d1) == PolynomialDegree::integral_t(d2); + } + else{ + return !(d1.isInteger() || d2.isInteger()); + } +} + +bool operator!=(const PolynomialDegree& d1, const PolynomialDegree& d2){ + return !(d1 == d2); +} + +} // namespace Algebra diff --git a/algebra/algebralib/src/SelectorSum.cpp b/algebra/algebralib/src/SelectorSum.cpp new file mode 100644 index 0000000..ec5bd9a --- /dev/null +++ b/algebra/algebralib/src/SelectorSum.cpp @@ -0,0 +1,244 @@ +#include +#include +namespace Algebra{ + + //useful auxilary method to eval by assignment when only have eval by vector + + vector assignmentToVector(const Algebra::VariableAssignment& assignment){ + vector retVal(assignment.size() * 2); + for (const auto& v : assignment){ + if (v.first.getIndex() > (int)retVal.capacity()) + retVal.resize(v.first.getIndex() * 2); + retVal[v.first.getIndex()] = v.second; + } + return retVal; + + } + + SelectorSum::SelectorSum() {} + + SelectorSum::SelectorSum(const vector& constraints, const vector& selectorVariableVector, const vector& selectorToConstraint, + const vector& selectorRelevant) : + constraints(constraints), + selectorToConstraint(selectorToConstraint), + selectorRelevant(selectorRelevant), + selectorVarNum(selectorVariableVector.size()), + selectorNum(selectorToConstraint.size()), + constraintNum(constraints.size()), + selectorNumBound(std::pow(2, selectorVariableVector.size())){ + ALGEBRALIB_ASSERT(selectorVariableVector.size() >0, "a variable vector of size 0"); + ALGEBRALIB_ASSERT(selectorToConstraint.size() == selectorRelevant.size(), "selectorToConstraint different size from selectorRelevant"); + + for (unsigned int i = 0; i < selectorVariableVector.size(); i++){ + selectorVars.push_back(selectorVariableVector[i].getIndex()); + selectorVariables.insert(selectorVariableVector[i]); + } + } + + //if selectorRelevant vector not given as input,assume all selectors are relevant + SelectorSum::SelectorSum(const vector& constraints, const vector& selectorVariableVector, const vector& selectorToConstraint) : + constraints(constraints), + selectorToConstraint(selectorToConstraint), + selectorVarNum(selectorVariableVector.size()), + selectorNum(selectorToConstraint.size()), + constraintNum(constraints.size()), + selectorNumBound(std::pow(2, selectorVariableVector.size())) { + + ALGEBRALIB_ASSERT(selectorVariableVector.size() >0, "a variable vector of size 0"); + + for (unsigned int i = 0; i < selectorVariableVector.size(); i++){ + selectorVars.push_back(selectorVariableVector[i].getIndex()); + selectorVariables.insert(selectorVariableVector[i]); + } + for (unsigned int i = 0; i < selectorToConstraint.size();i++) + selectorRelevant.push_back(true);//if selectorRelevant vector not given as input,assume all selectors are relevant + + } + + + + //evaluates a polynomial on a given assignment + FieldElement SelectorSum::eval(const vector& x)const{ + vector coeffs = getConstraintCoeffs(x); + FieldElement returnVal = zero(); + for (auto i = 0; i < constraintNum; i++) + returnVal += coeffs[i] * constraints[i].eval(x); + return returnVal; + + } + + + //evaluates a polynomial on a given assignment - the need to eval constraints[i] on assignment rather than x has to do with mess regarding double indices in Variable object + FieldElement SelectorSum::eval(const VariableAssignment& assignment)const{ + vector x = assignmentToVector(assignment); + vector coeffs = getConstraintCoeffs(x); + FieldElement returnVal = zero(); + for (auto i = 0; i < constraintNum; i++) + if (coeffs[i] != Algebra::zero()) + returnVal += coeffs[i] * constraints[i].eval(assignment); + return returnVal; + + } + + size_t SelectorSum::numVars()const{ + ALGEBRALIB_FATAL("Not implemented"); + } + + //the need for this method has to do with different selectors having same P_i, as explaing in top of hpp file + vector SelectorSum::getConstraintCoeffs(const vector& x)const{ + vector selectorVals = getSelectorVals(x); + vector coeffs; + for (auto i = 0; i < constraintNum; i++) + coeffs.push_back(zero()); + for (auto i = 0; i < selectorNum; i++) + if (selectorRelevant[i])//not all SelectorSum objects actually use all possible selector vals + coeffs[selectorToConstraint[i]] += selectorVals[i]; + return coeffs; + } + + + varSet SelectorSum::getUsedVariables()const { + varSet retVal(selectorVariables); + for (const auto& constraint : constraints){ + varSet v = constraint.getUsedVariables(); + retVal.insert(v.begin(), v.end()); + } + return retVal; + + } + + vector SelectorSum::getSelectorVals(const vector& x)const { + vector selectorVals(selectorNumBound); + selectorVals[0] = x[selectorVars[0]] + one(); + selectorVals[1] = x[selectorVars[0]]; + long curLength = 2; + + for (auto i = 1; i < selectorVarNum; i++){// in principle can do last iteration of loop separately to avoid rounding up selectorNum to a power of 2 + for (auto j = 0; j < curLength; j++){//copying the current vector.Then Mult each element of first copy by newVar +1, each element of second copy by newVar + //doing it in a `crooked' way to do just mult by newVar, and not also the mult by newVar +1 + selectorVals[j + curLength] = selectorVals[j] * x[selectorVars[i]]; + selectorVals[j] += selectorVals[j + curLength]; + } + curLength *= 2; + } + ////cout << "selectorVals:"; + //for (auto i = 0; i < selectorVals.size(); i++) + //// cout << selectorVals[i]; + //cout << endl; + return selectorVals; + } + + void SelectorSum::getConsts(map& FElemMap) const { + for (unsigned int i = 0; i < constraints.size(); i++){ + constraints[i].getConsts(FElemMap); + } + } + + string SelectorSum::asStringNewIndex(map& translate, bool isDegreePrint) const { + string res = ""; + bool startSelectorSum = true; + for (unsigned int i = 0; i < constraints.size(); i++){ + bool firstSelector = true; + for (auto j = 0; j < selectorNum; j++) { + if (selectorRelevant[j] && (selectorToConstraint[j] == i)) { + if (!firstSelector) { + res += " + "; + } + else { + if (startSelectorSum) { + res += "("; + startSelectorSum = false; + } + else { + res += " + ("; + } + firstSelector = false; + } + int k = j; + for (unsigned int counter = 0; counter < selectorVars.size(); counter++) { + int lsb = k & 1; + if (isDegreePrint) { + res += "(x[" + std::to_string(selectorVars[counter]) + "] + tmp_deg_type(0))"; + } + else { + res += "(x[" + std::to_string(selectorVars[counter]) + "] + " + (lsb == 0 ? "FElem1)" : "FElem0)"); + } + k >>= 1; + if (counter + 1 < selectorVars.size()){ + res += " * "; + } + } + } + } + if (!firstSelector) { + res += ") * "; + res += constraints[i].asStringNewIndex(translate, isDegreePrint); + } + } + return res; + } + + string SelectorSum::asString() const{ + string res = ""; + for (unsigned int i = 0; i < constraints.size(); i++){ + res += "(";//first printing the selectors multiplying the i'th constraint + for (auto j = 0; j < selectorNum; j++){ + if (selectorRelevant[j] && (selectorToConstraint[j] == i)) + res += "S_" + std::to_string(j); + } + res += ")"; + res += constraints[i].asString(); + res += " + "; + } + return res; + } + + //the need for this method, is because variable indices are reset in cs2BREX reduction, before passing constraints to BREX + void SelectorSum::setNewIndices(std::map& old2New){ + for (unsigned int i = 0; i < selectorVars.size(); i++) + selectorVars[i] = old2New[selectorVars[i]]; + + varSet newSelectorVariables;//this somehwat silly intermiedate variable, is because (at least VS) compiler insists there is problem when using auto& below + for (auto var : selectorVariables){ + var.setNewIndex(old2New[var.getIndex()]); + newSelectorVariables.insert(var); + } + selectorVariables = newSelectorVariables; + for (auto& constraint : constraints) + constraint.setNewIndices(old2New); + } + + + PolynomialDegree SelectorSum::getDegreeBound(const std::vector& inputDegrees)const { + PolynomialDegree selectorDeg(getSelectorDegreeBound(inputDegrees)); + PolynomialDegree maxDeg(-1); + for (auto i = 0; i < constraintNum; i++){ + maxDeg = max(maxDeg, constraints[i].getDegreeBound(inputDegrees)); + } + return degreeOfProduct(selectorDeg,maxDeg); + } + + + PolynomialDegree SelectorSum::getSelectorDegreeBound(const std::vector& inputDegrees)const { + PolynomialDegree prodDegree(0); + for (auto j = 0; j < selectorVarNum; j++){ + prodDegree = degreeOfProduct(prodDegree, inputDegrees[selectorVars[j]]); + + } + return prodDegree; + } + + unique_ptr SelectorSum::clone() const{ + return unique_ptr(new SelectorSum(*this)); + + } + + + + + + + + + +}//namespace Algebra diff --git a/algebra/algebralib/src/SubspacePolynomial.cpp b/algebra/algebralib/src/SubspacePolynomial.cpp new file mode 100644 index 0000000..3358689 --- /dev/null +++ b/algebra/algebralib/src/SubspacePolynomial.cpp @@ -0,0 +1,96 @@ + +/******************************************************************************************************/ +/************************************ SubspacePolynomial Class ***************************************/ +/******************************************************************************************************/ + +#include "algebraLib/SubspacePolynomial.hpp" +using std::vector; +namespace Algebra{ + + FieldElement sqr(FieldElement x){ + return x*x; + } + + /** Class Constructor - Computes the subspace polynomial that vanishes exactly over the span of spanSet*/ + + SubspacePolynomial::SubspacePolynomial(const elementsSet_t& spanSet):LinearizedPolynomial(computeVanishingPoly(spanSet)){ + + } + + + /** Returns the vector of coefficients of the subspace polynomial vanishing on the span of spanSet={e1,..,ek} + * For simplicity, let us first describe an alg that would work assuming {e1,..ek} are lin. independent: + * The algorithm would inductively computes the (coeffs of the) subspace polynomial P_i of the subspace spanned by {e_1,..,e_i}. + * For i=1, this is the polynomial x^2 + e1*x. Assume we have computed P_{i-1}. + * it turns out that P_i is P_{i-1} composed from the outside with x^2 + P_{i-1}(e_i)*x + * this is the formula the code implements. + All this was assuming {e1,..,ek} were linearly independent. + Now, to remove this assumption, before constructing the next P_i we check if the current e_i is linearly dependent + on {e1,..,e_{i-1}} - we do this by simply checking if P_{i-1}(e_i) =0. + If so, we simply let P_i= P_{i-1}. + Otherwise, we derive P_i using the formula described above. + */ + vector SubspacePolynomial::computeVanishingPoly(const elementsSet_t& spanSet) { + vector coefficients ; + //if spanSet is empty we return the id polynomial + if (spanSet.size() == 0){ + vector idPoly(1); + idPoly[0] = one(); + return idPoly; + } + + + //Initialzing as the subspace poly of the space {0} - which is x + coefficients.push_back(one()); + FieldElement c, eiPower; + unsigned int i = 0, j; + elementsSet_t::iterator z = spanSet.begin(); + while (z != spanSet.end()){ + //compute c= P_{i-1}(e_i) + c = zero(); + + eiPower = *z; //eiPower starts as ei + for (j = 0; j < coefficients.size(); j++) { + c += coefficients[j] * eiPower; + eiPower = sqr(eiPower); + } + //Now check if c is non-zero - which means e_i is independent of previous ej's. + if (!(c == zero())){ + //if so, update P_i to be zero on the extended span of {e1,..ei} + i++; + coefficients.push_back(one()); + for (j = i - 1; j >= 1; j--) { + coefficients[j] = c*coefficients[j] + sqr(coefficients[j - 1]); + } + coefficients[0] = c*coefficients[0]; + + } + z++; + } + return coefficients; + } + + + + + + /** Testing purposes */ + //void SubspacePolynomial::print() const { + // cout << "Vanishing Polynomial: " << endl; + // + // if (coefficients == NULL) { + // cout << "Empty object!" << endl; + // return; + // } + // + // for (unsigned long i = 0; i(0)) + // cout << "Constant Factor: " << constantFactor << endl; + //} + + + +}//namespace Algebra diff --git a/algebra/algebralib/src/UnivariatePolynomialGeneral.cpp b/algebra/algebralib/src/UnivariatePolynomialGeneral.cpp new file mode 100644 index 0000000..2da700b --- /dev/null +++ b/algebra/algebralib/src/UnivariatePolynomialGeneral.cpp @@ -0,0 +1,363 @@ +#include "algebraLib/UnivariatePolynomialGeneral.hpp" +#include "algebraLib/FFT.hpp" +#include "algebraLib/ErrorHandling.hpp" + +#include + +#include +#include + +using std::unique_ptr; +using std::vector; +using std::max; + +namespace Algebra{ + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const UnivariatePolynomialInterface& src):polynomial_(src.getCoefficients()){ } + +unique_ptr UnivariatePolynomialGeneral::clone()const{ + return unique_ptr(new UnivariatePolynomialGeneral(*this)); +} + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const FieldElement& constant):polynomial_(1) { + setCoefficient(0,constant); +} + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const vector& coeffs):polynomial_(coeffs) {}; +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(vector&& coeffs):polynomial_(std::move(coeffs)) {}; + +namespace{ +//multiply poly by $x-r$ +void multiplyByRootPoly(vector& poly,const FieldElement& r){ + poly.resize(poly.size()+1,zero()); + for(int i=poly.size()-1; i>0; i--){ + poly[i] = poly[i-1] + r*poly[i]; + } + poly[0] *= r; +} +} + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const elementsSet_t& roots):polynomial_(1) { + polynomial_[0] = one(); + + /** + * For each root \f$r\f$ generate + * the affine polynomial \f$q(x) = x - r\f$ + * and multiply current polynomial + * by it + */ + for(const auto& element : roots){ + multiplyByRootPoly(polynomial_,element); + } +} + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const vector& evaluation, const vector& orderedBasis, const FieldElement& spaceShift): polynomial_(IFFT(evaluation,orderedBasis,spaceShift)){}; + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const vector& evaluation, const AffineSpace& evaluationSpace): polynomial_(IFFT(evaluation,evaluationSpace.getBasis(),evaluationSpace.getAffineShift())){}; + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const evaluation_t& evaluationTabel, const elementsSet_t& spaceBasis){ + + const short basisSize = spaceBasis.size(); + const size_t spaceSize = (1< orderedBasis; + for(const auto& b : spaceBasis) orderedBasis.push_back(b); + + //construct the evaluation + vector vals(spaceSize); + for(size_t i=0; (i>>basisSize)==0; i++){ + const auto iter = evaluationTabel.find(getSpaceElementByIndex(orderedBasis, zero(), i)); + ALGEBRALIB_ASSERT(iter != evaluationTabel.end(),"Bad evaluation table, imposible interpolation"); + vals[i] = iter->second; + } + + //compute the IFFT + polynomial_ = IFFT(vals,orderedBasis,zero()); +} + +UnivariatePolynomialGeneral::UnivariatePolynomialGeneral(const evaluation_t& evaluationTabel){ + + //use Lagrange interpolation + for(const auto& point : evaluationTabel){ + + //construct current lagrange poly + UnivariatePolynomialGeneral lagrangePoly(one()); + FieldElement denumenator = one(); + for(const auto& other_point : evaluationTabel){ + if(other_point.first != point.first){ + denumenator *= (point.first - other_point.first); + multiplyByRootPoly(lagrangePoly.polynomial_,other_point.first); + } + } + + lagrangePoly.multiply(point.second/denumenator); + + //add to curr poly + add(lagrangePoly); + } +} + + +FieldElement UnivariatePolynomialGeneral::eval(const FieldElement& x)const { + //use horner evaluation + FieldElement res = zero(); + for(int i=polynomial_.size()-1; i>=0; i--){ + res = res*x + polynomial_[i]; + } + + return res; +} + +std::vector UnivariatePolynomialGeneral::eval(const std::vector& orderedBasis, const FieldElement& shift)const{ + return FFT(polynomial_,orderedBasis,shift); +} + +const std::vector UnivariatePolynomialGeneral::getCoefficients()const{ + return polynomial_; +} + +namespace{ + ///Returns log in base 2 + double Log2( double n ) { + return log(n) / log((double)2); // log(n)/log(2) is ::Infrastructure::Log2. + } +} + +UnivariatePolynomialInterface* UnivariatePolynomialGeneral::eval(const UnivariatePolynomialInterface& p)const{ + const PolynomialDegree::integral_t maxDeg(degreeOfComposition(getDegree(), p.getDegree())); + + const size_t evalSpaceDim = ceil(Log2(maxDeg+1)); + const elementsSet_t basisSet = getStandartBasis(evalSpaceDim); + const vector evalSpaceBasis(basisSet.begin(),basisSet.end()); + vector evaluation(1L<= polynomial_.size()){ + polynomial_.resize(index+1,zero()); + } + + polynomial_[index] = coefficient; +} + +FieldElement UnivariatePolynomialGeneral::getCoefficient(const unsigned int index)const { + if(index >= polynomial_.size()){ + return zero(); + } + return polynomial_[index]; +} + +PolynomialDegree UnivariatePolynomialGeneral::getDegree()const { + + //if there are no coefficients this is the ZERO polynomial + if(polynomial_.size() == 0){ + return PolynomialDegree::getZeroPolyDegree(); + } + + //find the highest non zero coefficient index + for(int i=polynomial_.size()-1; i>=0; i--){ + if (polynomial_[i] != zero()){ + return PolynomialDegree(i); + } + } + + //no non-zero coefficients found + return PolynomialDegree::getZeroPolyDegree(); +} + +unique_ptr UnivariatePolynomialGeneral::divideByMe(const UnivariatePolynomialInterface& dividend)const { + /* + * Division implemented using the algorithem from Wikipedia: + * https://en.wikipedia.org/wiki/Polynomial_long_division + * + * function n / d: + * require d ≠ 0 + * q ← 0 + * r ← n # At each step n = d × q + r + * while r ≠ 0 AND degree(r) ≥ degree(d): + * t ← lead(r)/lead(d) # Divide the leading terms + * q ← q + t + * r ← r − t * d + * return (q, r) + */ + + const PolynomialDegree enumDeg = dividend.getDegree(); + const PolynomialDegree denumDeg = getDegree(); + + ALGEBRALIB_ASSERT(denumDeg.isInteger(), "Divisor polynomial can not be the ZERO polynomial"); + + //if the enumerator has lower degree then denumerator, return the ZERO polynomial + if(enumDeg < denumDeg){ + return unique_ptr(new UnivariatePolynomialGeneral()); + } + + const long long enumDegInt = PolynomialDegree::integral_t(enumDeg); + const long long denumDegInt = PolynomialDegree::integral_t(denumDeg); + const FieldElement denumLeadInv = one()/polynomial_[denumDegInt]; + + vector q((enumDegInt - denumDegInt) + 1, zero()); + vector r = dividend.getCoefficients(); + long long r_deg = enumDegInt; + + while (r_deg >= denumDegInt){ + const FieldElement t_val = r[r_deg]; + const int t_shift = r_deg-denumDegInt; + + q[t_shift] += t_val; + + for(int i=0; i< denumDegInt; i++){ + r[i+t_shift] += t_val*polynomial_[i]; + } + + r_deg = -1; + for(int i=denumDegInt+t_shift-1; i>=0; i--){ + if(r[i] != zero()){ + r_deg = i; + break; + } + } + } + + return unique_ptr(new UnivariatePolynomialGeneral(q)); +} + +void UnivariatePolynomialGeneral::multiply(const UnivariatePolynomialGeneral& other){ + //use FFT for multiplication: + //first evaluate both polynomials on a big enough space, + //multiplicate the evaluations, + //and interpolate it back using IFFT + + const PolynomialDegree degreeBound = degreeOfProduct(getDegree(),other.getDegree()); + + //if expected degree is -infinity, this is the zero polynomial + if(!degreeBound.isInteger()){ + polynomial_.resize(0); + return; + } + + //else find a big enough evaluation space + const size_t space_dim = ceil(log2(1+ PolynomialDegree::integral_t(degreeBound))); + const auto basis = getStandartBasis(space_dim); + vector orderedBasis(basis.begin(),basis.end()); + + //evaluate both polys + vector eval1 = FFT(polynomial_,orderedBasis,zero()); + const vector eval2 = FFT(other.polynomial_,orderedBasis,zero()); + + + //compute the product evaluation +#pragma omp parallel for + for(unsigned long long i=0; i< eval2.size(); i++){ + eval1[i] *= eval2[i]; + } + + //interpolate to get the coefficients + polynomial_ = IFFT(eval1,orderedBasis,zero()); +} + +void UnivariatePolynomialGeneral::multiply(const FieldElement& factor){ + + //multiply the coefficients pointwise +#pragma omp parallel for + for(unsigned long long i=0; i< polynomial_.size() ; i++){ + polynomial_[i] *= factor; + } +} + +void UnivariatePolynomialGeneral::multiply_by_monomial(const FieldElement& factor, const size_t deg){ + multiply(factor); + const vector padding(deg,zero()); + polynomial_.insert(polynomial_.begin(),padding.begin(),padding.end()); +} + +void UnivariatePolynomialGeneral::add(const UnivariatePolynomialGeneral& other){ + const PolynomialDegree degOfOther = other.getDegree(); + + //if the other is the zero poly, nothing should be done + if(!degOfOther.isInteger())return; + + //otherwise, the other polynomial has effective coefficients. + //extend corrent poly len if needed + const size_t otherNumCoeffs = 1+PolynomialDegree::integral_t(degOfOther); + if(polynomial_.size() < otherNumCoeffs){ + polynomial_.resize(otherNumCoeffs,zero()); + } + + //add the coefficients pointwise +#pragma omp parallel for + for(unsigned long long i=0; i< otherNumCoeffs ; i++){ + polynomial_[i] += other.polynomial_[i]; + } +} + +UnivariatePolynomialGeneral operator+(const UnivariatePolynomialGeneral& a, const UnivariatePolynomialGeneral& b) { + UnivariatePolynomialGeneral res(a); + res.add(b); + return res; +} + +UnivariatePolynomialGeneral& operator+=(UnivariatePolynomialGeneral& x, const UnivariatePolynomialGeneral& b) { + x.add(b); + return x; +} + +UnivariatePolynomialGeneral operator-(const UnivariatePolynomialGeneral& a, const UnivariatePolynomialGeneral& b) +{ return a+b; } + +UnivariatePolynomialGeneral operator*(const UnivariatePolynomialGeneral& a, const UnivariatePolynomialGeneral& b) { + UnivariatePolynomialGeneral res(a); + res.multiply(b); + return res; +} + +UnivariatePolynomialGeneral& operator*=(UnivariatePolynomialGeneral& x, const UnivariatePolynomialGeneral& b) { + x.multiply(b); + return x; +} + +UnivariatePolynomialGeneral operator*(const UnivariatePolynomialGeneral& poly, const FieldElement& factor) { + UnivariatePolynomialGeneral res(poly); + res.multiply(factor); + return res; +} + +UnivariatePolynomialGeneral operator*(const FieldElement& factor, const UnivariatePolynomialGeneral& poly) { + return poly*factor; +} + +UnivariatePolynomialGeneral& operator*=(UnivariatePolynomialGeneral& poly, const FieldElement& factor) { + poly.multiply(factor); + return poly; +} + +bool operator==(const UnivariatePolynomialGeneral& a, const UnivariatePolynomialGeneral& b) { + //if the degrees differ, they can't be the same + const PolynomialDegree aDeg = a.getDegree(); + const PolynomialDegree bDeg = b.getDegree(); + if(aDeg != bDeg)return false; + + const size_t numCoeffs = max(PolynomialDegree::integral_t(0),1+PolynomialDegree::integral_t(aDeg)); + for(size_t i=0; i< numCoeffs; i++){ + if(a.getCoefficient(i) != b.getCoefficient(i))return false; + } + + //if difference not found + return true; +} + +bool operator!=(const UnivariatePolynomialGeneral& a, const UnivariatePolynomialGeneral& b) +{ return !(a == b); } + +std::ostream& operator<<(std::ostream& s, const UnivariatePolynomialGeneral& a) { + for(int i=0; i<= PolynomialDegree::integral_t(a.getDegree()); i++){ + s << a.getCoefficient(i); + } + return s; +} + +} //namespace Algebra diff --git a/algebra/algebralib/src/novelFFT.cpp b/algebra/algebralib/src/novelFFT.cpp new file mode 100644 index 0000000..4067439 --- /dev/null +++ b/algebra/algebralib/src/novelFFT.cpp @@ -0,0 +1,232 @@ +#include "algebraLib/novelFFT.hpp" +#include "algebraLib/ErrorHandling.hpp" +#include +#include + +namespace Algebra{ + +using std::vector; + +namespace{ + vector calc_X_exp(const vector& orderedBasis){ + vector X_exp; + { + for(unsigned int i=0; i novelIFFT(const vector orderedBasis, const vector X_exp, vector&& polysVals,const size_t numPolys, const size_t width){ + const unsigned short basisSize = orderedBasis.size(); + const size_t spaceSize = 1UL< X_exp_precomp(1UL<<(len_c-1)); +#pragma omp parallel for + for(unsigned long long c=0; c < X_exp_precomp.size(); c++){ + const FieldElement c_elem = getSpaceElementByIndex(orderedBasis,zero(),c<<(currShift_c+1)); + X_exp_precomp[c] = X_exp[i].eval(c_elem); + } + +#pragma omp parallel for + for(unsigned long long mc=0; mc < (spaceSize>>1); mc++){ + + const size_t m = mc & currMask_m; + const size_t c = (mc>>(currShift_c))<<1; + + + for(size_t polyIdx = 0; polyIdx < numPolys; polyIdx++){ + FieldElement* currPoly = &polysVals[polyIdx]; + //handle case (18) + { + const size_t mc_case18 = m | ((c|1UL)<>1] * currPoly[width*prevIdx2]; + } + } + } + } + + return polysVals; + } + + vector convertPolysBasis(const vector orderedBasis, const vector X_exp, vector>&& polysCoeffs, const size_t width, const FieldElement& pad_value){ + + ALGEBRALIB_ASSERT(width >= polysCoeffs.size(), "Width must be at least as the number of polys"); + const size_t numPolys = polysCoeffs.size(); + const size_t spaceSize = 1UL< res(width * spaceSize, pad_value); + { + FFF::Element* basis_vec = (FFF::Element*)(&(orderedBasis[0])); + FFF::Element shift_fff(zero()); + FFF::Basis basis(basis_vec, orderedBasis.size(),shift_fff); + FFF::FFT fftInstance(basis,FFF::FFT_OP); + +#pragma omp parallel for + for(unsigned int i=0; i< numPolys; i++){ + + vector& currCoeffs = polysCoeffs[i]; + + ALGEBRALIB_ASSERT(currCoeffs.size() <= spaceSize, "FFT is supported only for evaluation spaces of size at least as the polynomial degree"); + + if(currCoeffs.size() < spaceSize){ + currCoeffs.resize(spaceSize,zero()); + } + + auto c_poly = (FFF::Element*)(&currCoeffs[0]); + fftInstance.AlgFFT(&c_poly,spaceSize); + + //copy to res matrix + for(unsigned long long x=0; x< spaceSize; x++){ + res[x*width + i] = currCoeffs[x]; + } + } + } + + //execute novel IFFT on evaluation to get coefficients in novel polynomial basis + return novelIFFT(orderedBasis,X_exp,std::move(res),numPolys,width); + } + +} // anonymous namespace + +novelFFT::novelFFT(const vector& orderedBasis, const vector X_exp, std::vector>&& polysCoeffs, const size_t width, const FieldElement& pad_value): + X_exp_(X_exp), + polys_(convertPolysBasis(orderedBasis,X_exp,std::move(polysCoeffs),width,pad_value)), + orderedBasis_(orderedBasis), + numPolys_(polysCoeffs.size()), + width_(width){}; + +novelFFT::novelFFT(const vector& orderedBasis, const vector X_exp, std::vector&& polyEval): + X_exp_(X_exp), + polys_(novelIFFT(orderedBasis,X_exp,std::move(polyEval),1UL,1UL)), + orderedBasis_(orderedBasis), + numPolys_(1), + width_(1){}; + +novelFFT::novelFFT(const vector& orderedBasis, vector&& srcEval) : + novelFFT(orderedBasis, calc_X_exp(orderedBasis), std::move(srcEval)){}; + +novelFFT::novelFFT(const vector& orderedBasis, vector>&& polysCoeffs, const size_t width, const FieldElement& pad_value) : + novelFFT(orderedBasis, calc_X_exp(orderedBasis), std::move(polysCoeffs), width, pad_value){}; + +void novelFFT::FFT(const vector& affineShift, FieldElement* dst, const size_t diff_coset)const{ + + const unsigned short basisSize = orderedBasis_.size(); + const size_t spaceSize = 1UL< 0){ + for(unsigned int cosetIdx = 0; cosetIdx < affineShift.size(); cosetIdx++){ + memcpy((char*)(dst + (cosetIdx*diff_coset)) + (bufferBytesLen - blockRemeinder), (char*)(&polys_[0]) + (bufferBytesLen - blockRemeinder), blockRemeinder); + } + } + + } + + //execute the FFT + { + for (int i=basisSize-1; i >= 0; i--){ + const unsigned short currShift_c = i; + const size_t currMask_m = (1UL<> X_exp_precomp(affineShift.size(),vector(numPrecompute)); +#pragma omp parallel for + for(unsigned long long expIdx=0; expIdx < (numShifts<> logNumPrecompute; + const FieldElement c_elem = getSpaceElementByIndex(orderedBasis_,affineShift[cosetId],c<<(currShift_c+1)); + X_exp_precomp[cosetId][c] = X_exp_[i].eval(c_elem); + } + +#pragma omp parallel for + for(unsigned long long mc=0; mc < spaceSize/2; mc++){ + + const size_t m = mc & currMask_m; + const size_t c = (mc>>(currShift_c))<<1; + + { + const size_t mc_case17 = m | (c<>1; + for(size_t cosetId=0; cosetId < affineShift.size(); cosetId++){ + + const FieldElement Xpre = X_exp_precomp[cosetId][cs1]; + const FFF::Element XpreElem = *(FFF::Element*)(&Xpre); + + const size_t cosetOffset = diff_coset*cosetId; + FieldElement* baseVec = dst + cosetOffset; + FFF::Element* currVecFFF = (FFF::Element*)(baseVec); + + // + // Irreducible specific implementation + // + FFF::Element::do_FFT_step(XpreElem, &currVecFFF[mc_case18index], &currVecFFF[mc_case17index],numPolys_); + + + /* + * General code - less field specific optimizations + */ + + //const size_t ub = polys_.size() * diff_poly_fixed; + //for(size_t polyOffset=0; polyOffset < ub; polyOffset+=(diff_poly_fixed*2)){ + //FieldElement* currVec = baseVec + polyOffset; + + //handle case (17) + //currVec[mc_case17index] = currVec[mc_case17index] + Xpre * currVec[mc_case18index]; + + //handle case (18) + //currVec[mc_case18index] = currVec[mc_case17index] + currVec[mc_case18index]; + //} + } + } + } + } + } +} + +} // namespace Algebra diff --git a/algebra/algebralib/src/variable.cpp b/algebra/algebralib/src/variable.cpp new file mode 100644 index 0000000..8d56900 --- /dev/null +++ b/algebra/algebralib/src/variable.cpp @@ -0,0 +1,270 @@ +#include "algebraLib/variable.hpp" +#include "algebraLib/ErrorHandling.hpp" + +#include +#include + +using std::vector; + +namespace Algebra { + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class Variable ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +VarIndex_t Variable::nextFreeIndex_ = 0; + +Variable::Variable(const std::string& name) : + index_(nextFreeIndex_++), + newIndex_(-1), + name_(name){ + ALGEBRALIB_ASSERT(nextFreeIndex_ > 0, ALGEBRALIB_FMT("Variable index overflow has occured, maximum number of " + "Variables is %lu", ULONG_MAX)); +} + +std::string Variable::name() const { return name_; } + +::std::string Variable::newIndexName() const { + ALGEBRALIB_ASSERT(newIndex_ >= 0, "Variable eval: newIndex_ is negative"); + return "x[" + std::to_string(newIndex_) + "]"; +} + +FElem Variable::eval(const VariableAssignment& assignment) const { + try { + return assignment.at(*this); + } + catch (::std::out_of_range) { + ALGEBRALIB_FATAL(ALGEBRALIB_FMT("Attempted to evaluate unassigned Variable \"%s\", idx:%lu", name().c_str(), index_)); + } +} + +FElem Variable::eval(const std::vector& assignment) const { + ALGEBRALIB_ASSERT(newIndex_ >= 0, "Variable eval: newIndex_ is negative"); + ALGEBRALIB_ASSERT(newIndex_ < (int)assignment.size(), "Variable eval: not enough FElems in the vector"); + return assignment[newIndex_]; +} + +VarIndex_t Variable::getNewIndex() const { + //ALGEBRALIB_ASSERT(newIndex_ < 0, "trying to getNewIndex from varialbe when it is negative - meaning probably not initialized"); + return newIndex_; +} + +void Variable::setNewIndex(VarIndex_t newIndex){ + ALGEBRALIB_ASSERT(newIndex >= 0, "Variable eval: newIndex_ is negative"); + newIndex_ = newIndex; +} + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class VariableArray ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + + +VariableArray::VariableArray(const ::std::string& name) : VariableArrayContents(), name_(name) {} +VariableArray::VariableArray(const int size, const ::std::string& name) : VariableArrayContents() { + for (int i = 0; i < size; ++i) { + std::string variableName = name + "[" + std::to_string(i) + "]"; + push_back(Variable(variableName)); + } +} +::std::string VariableArray::name() const { + return name_; +} + + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class LinearTerm ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +::std::string LinearTerm::asString() const { + if (coeff_ == Algebra::one()) { return variable_.name(); } + else if (coeff_ == Algebra::zero()) { return Algebra::zero().asString(); } + else { return coeff_.asString() + "*" + variable_.name(); } +} + +::std::string LinearTerm::asStringNewIndex(map& translate, bool isDegreePrint) const { + ALGEBRALIB_ASSERT(translate.find(coeff_) != translate.end(), "LinearTerm: Coeff doesn't exist in the translation vector"); + if (coeff_ == Algebra::one()) { return variable_.newIndexName(); } + if (coeff_ == Algebra::zero()) { + if (isDegreePrint) { + return "tmp_deg_type(0)"; + } + return "FElem0"; } + else { + if (isDegreePrint) { + return "tmp_deg_type(0) * " + variable_.newIndexName(); + } + return "FElem" + std::to_string(translate[coeff_]) + "*" + variable_.newIndexName(); } +} + +FElem LinearTerm::eval(const VariableAssignment& assignment) const { + return coeff_ * variable_.eval(assignment); +} + +FElem LinearTerm::eval(const std::vector& assignment) const { + return coeff_ * variable_.eval(assignment); +} + +void LinearTerm::setNewIndices(std::map& old2New){ + variable_.setNewIndex(old2New[variable_.getIndex()]); +} + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class LinearCombination ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +LinearCombination::LinearCombination(const LinearTerm& linearTerm) : linearTerms_(), constant_(Algebra::zero()) { + if (linearTerm.getCoeff() != Algebra::zero()) { + linearTerms_.emplace_back(linearTerm); + } +} + +LinearCombination& LinearCombination::operator+=(const LinearCombination& other) { + vector lt; + for (const LinearTerm& l : linearTerms_) { + if (l.getCoeff() != Algebra::zero()) { + lt.emplace_back(l); + } + } + for (const LinearTerm& l : other.linearTerms_) { + if (l.getCoeff() != Algebra::zero()) { + lt.emplace_back(l); + } + } + linearTerms_ = lt; + constant_ += other.constant_; + return *this; +} + +LinearCombination& LinearCombination::operator*=(const FElem& other) { + if (other == Algebra::zero()) { + linearTerms_.clear(); + constant_ = Algebra::zero(); + return *this; + } + constant_ *= other; + for (LinearTerm& lt : linearTerms_) { + lt *= other; + } + return *this; +} + +FElem LinearCombination::eval(const VariableAssignment& assignment) const { + FElem evaluation = constant_; + for (const LinearTerm& lt : linearTerms_) { + evaluation += lt.eval(assignment); + } + return evaluation; +} + +FElem LinearCombination::eval(const std::vector& assignment) const{ + FElem evaluation = constant_; + for (const LinearTerm& lt : linearTerms_) { + evaluation += lt.eval(assignment); + } + return evaluation; +} + +std::string LinearCombination::asString() const { + if (linearTerms_.size() == 0 ) { + return "(" + constant_.asString() + ")"; + } + else { + std::string retval = "("; + unsigned int i; + for (i = 0; i+1 < linearTerms_.size(); i++) { + retval += linearTerms_[i].asString() + "+"; + } + retval += linearTerms_[i].asString(); + if (constant_ != Algebra::zero()) { + retval += "+" + constant_.asString(); + } + retval += ")"; + return retval; + } +} + +std::string LinearCombination::asStringNewIndex(map& translate, bool isDegreePrint) const { + if (linearTerms_.size() == 0) { + ALGEBRALIB_ASSERT(translate.find(constant_) != translate.end(), "LinearCombination: Coeff doesn't exist in the translation vector"); + if (isDegreePrint) { + return "(tmp_deg_type(0))"; + } + return "(FElem" + std::to_string(translate[constant_]) +")"; + } + else { + std::string retval = "("; + unsigned int i; + for (i = 0; i+1 < linearTerms_.size(); i++) { + retval += linearTerms_[i].asStringNewIndex(translate,isDegreePrint) + "+"; + } + retval += linearTerms_[i].asStringNewIndex(translate, isDegreePrint); + if (constant_ != Algebra::zero()) { + if (isDegreePrint) { + retval += " + tmp_deg_type(0)"; + } + else { + retval += " + FElem" + std::to_string(translate[constant_]); + } + } + retval += ")"; + return retval; + } +} + +const Variable::set LinearCombination::getUsedVariables() const { + Variable::set retSet; + for (const LinearTerm& lt : linearTerms_) { + retSet.insert(lt.variable()); + } + return retSet; +} + +void LinearCombination::setNewIndices(std::map& old2New){ + for (LinearTerm& l : linearTerms_){ + l.setNewIndices(old2New); + } +} + +PolynomialDegree LinearCombination::getDegreeBound(const std::vector& inputDegrees) const{ + PolynomialDegree maxDegree(0); + for (const auto& t : linearTerms_){ + maxDegree = std::max(maxDegree, inputDegrees[t.variable().getNewIndex()]); + } + return maxDegree; +} +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + + +} //namespace diff --git a/examples-tinyram/coNP-subsetsum-noRAM.asm b/examples-tinyram/coNP-subsetsum-noRAM.asm new file mode 100644 index 0000000..29b911b --- /dev/null +++ b/examples-tinyram/coNP-subsetsum-noRAM.asm @@ -0,0 +1,22 @@ +MOV r0 r0 1 +CMPE r0 r0 8 +CJMP r0 r0 20 +MOV r1 r0 0 +MOV r2 r0 r0 +MOV r3 r0 0 +AND r4 r2 1 +CMPE r0 r4 0 +CJMP r0 r0 11 +RESERVED_OPCODE_24 r4 r0 r3 +ADD r1 r1 r4 +SHR r2 r2 1 +CMPE r0 r2 0 +CJMP r0 r0 16 +ADD r3 r3 1 +JMP r0 r0 6 +CMPE r0 r1 0 +CJMP r0 r0 21 +ADD r0 r0 1 +JMP r0 r0 1 +MOV r0 r0 0 +ANSWER r0 r0 r0 diff --git a/examples-tinyram/coNP-subsetsum-withRAM.asm b/examples-tinyram/coNP-subsetsum-withRAM.asm new file mode 100644 index 0000000..45cd47b --- /dev/null +++ b/examples-tinyram/coNP-subsetsum-withRAM.asm @@ -0,0 +1,93 @@ +MOV r9 r0 40598 +STOREW r9 r0 65400 +MOV r9 r0 9330 +STOREW r9 r0 65401 +MOV r0 r0 65400 +MOV r1 r0 0 +MOV r9 r0 0 +STOREW r9 r0 r1 +ADD r2 r1 32768 +STOREW r9 r0 r2 +MOV r2 r0 r1 +ADD r4 r1 1 +MOV r5 r0 r4 +MOV r8 r0 1 +ADD r9 r0 1 +LOADW r3 r0 r0 +JMP r0 r0 47 +ADD r0 r0 1 +CMPE r0 r9 r0 +CJMP r0 r0 63 +LOADW r3 r0 r0 +SHL r8 r8 1 +MOV r5 r0 r4 +JMP r0 r0 47 +ADD r7 r4 32768 +STOREW r6 r0 r7 +ADD r4 r4 1 +CMPE r0 r5 r1 +CNJMP r0 r0 39 +CMPE r0 r5 r2 +CJMP r0 r0 17 +LOADW r6 r0 r2 +ADD r6 r6 r3 +STOREW r6 r0 r4 +ADD r6 r2 32768 +LOADW r6 r0 r6 +XOR r6 r6 r8 +ADD r2 r2 1 +JMP r0 r0 24 +CMPE r0 r5 r2 +CNJMP r0 r0 47 +LOADW r6 r0 r1 +STOREW r6 r0 r4 +ADD r6 r1 32768 +LOADW r6 r0 r6 +ADD r1 r1 1 +JMP r0 r0 24 +LOADW r6 r0 r1 +LOADW r7 r0 r2 +ADD r7 r7 r3 +CMPG r0 r6 r7 +CJMP r0 r0 57 +STOREW r6 r0 r4 +ADD r6 r1 32768 +LOADW r6 r0 r6 +ADD r1 r1 1 +JMP r0 r0 24 +STOREW r7 r0 r4 +ADD r6 r2 32768 +LOADW r6 r0 r6 +XOR r6 r6 r8 +ADD r2 r2 1 +JMP r0 r0 24 +CMPA r0 r1 16384 +CJMP r0 r0 67 +MOV r1 r0 16384 +JMP r0 r0 6 +MOV r0 r0 2 +LOADW r2 r0 r0 +LOADW r3 r0 r1 +ADD r4 r2 r3 +CMPE r0 r4 0 +CJMP r0 r0 86 +CMPG r0 r4 0 +CJMP r0 r0 80 +CMPE r0 r1 16386 +CJMP r0 r0 85 +ADD r1 r1 1 +LOADW r3 r0 r1 +JMP r0 r0 70 +CMPE r0 r0 0 +CJMP r0 r0 85 +SUB r0 r0 1 +LOADW r2 r0 r0 +JMP r0 r0 70 +ANSWER r0 r0 0 +ADD r2 r0 32768 +LOADW r2 r0 r2 +ADD r3 r1 32768 +LOADW r3 r0 r3 +SHL r3 r3 1 +XOR r2 r2 r3 +ANSWER r0 r0 r2 diff --git a/examples-tinyram/collatz.asm b/examples-tinyram/collatz.asm new file mode 100644 index 0000000..6f47737 --- /dev/null +++ b/examples-tinyram/collatz.asm @@ -0,0 +1,13 @@ +MOV r8 r0 3 +CMPE r0 r8 1 +CJMP r0 r0 12 +ADD r9 r9 1 +AND r7 r8 1 +CMPE r0 r7 0 +CJMP r0 r0 10 +SHL r7 r8 1 +ADD r7 r7 r8 +ADD r8 r7 1 +SHR r8 r8 1 +JMP r0 r0 1 +ANSWER r0 r0 r9 diff --git a/libstark/Makefile b/libstark/Makefile new file mode 100644 index 0000000..3ca7b26 --- /dev/null +++ b/libstark/Makefile @@ -0,0 +1,39 @@ +CC=g++ +CPPFLAGS=-std=c++14 +CFLAGS=-O3 -g -Wall -fmessage-length=0 -fopenmp -mavx -maes -mtune=native + +INCFLAGS=-Isrc -I$(ALGEBRAINC) -I$(FFTINC) + +SRCDIR = src +SRCEXT = cpp +OBJDIR = $(BLDDIR)/obj + +SRCS := $(shell find $(SRCDIR) -name '*.$(SRCEXT)') +SRCDIRS := $(shell find . -name '*.$(SRCEXT)' -exec dirname {} \; | uniq) +OBJS := $(patsubst %.$(SRCEXT),$(OBJDIR)/%.o,$(SRCS)) + +TARGET=$(BLDDIR)/libstark.a +all: $(TARGET) + +$(TARGET): buildrepo $(OBJS) +# @echo 'Building target: $@' +# @echo 'Invoking: GCC Linker' + ar -r "$@" $(OBJS) $(LIBS) +# @echo 'Finished building target: $@' + +$(OBJDIR)/%.o: %.$(SRCEXT) +# @echo "$(CC) $(CFLAGS) $(CPPFLAGS) $(INCFLAGS) -c -o "$@" "$<"" + $(CC) $(CFLAGS) $(CPPFLAGS) $(INCFLAGS) -c -o "$@" "$<" + +clean: + $(RM) -f $(TARGET) $(OBJS) $(DEPS) + +buildrepo: + $(call make-repo) + +define make-repo +for dir in $(SRCDIRS); \ + do \ + mkdir -p $(OBJDIR)/$$dir; \ + done +endef diff --git a/libstark/src/common/Algebra/FieldElementPredicate.hpp b/libstark/src/common/Algebra/FieldElementPredicate.hpp new file mode 100644 index 0000000..d0eda5c --- /dev/null +++ b/libstark/src/common/Algebra/FieldElementPredicate.hpp @@ -0,0 +1,14 @@ +#include + +#ifndef __FieldElementPredicate_HPP +#define __FieldElementPredicate_HPP + +namespace Algebra { +class FieldElementPredicate { +public: + virtual bool test(const FieldElement&)const =0; + virtual ~FieldElementPredicate(){}; +}; +} // namespace Algebra + +#endif // __FieldElementPredicate_HPP diff --git a/libstark/src/common/Algebra/FiniteSetInterface.hpp b/libstark/src/common/Algebra/FiniteSetInterface.hpp new file mode 100644 index 0000000..157214e --- /dev/null +++ b/libstark/src/common/Algebra/FiniteSetInterface.hpp @@ -0,0 +1,40 @@ +#include "FieldElementPredicate.hpp" +#include + +#include + +#ifndef __SET_INTERFACE_H +#define __SET_INTERFACE_H + +namespace Algebra{ + +class FiniteSetInterface { +public: + /** + * @brief Returns true iff there exists an element in the set that satisfies some predicate + * @param pred the predicate + * @return True iff an element that satisfies the predicate exists in the set + */ + virtual bool exist(const std::unique_ptr& pred)const = 0; + + /** + * Returns the size of the set + */ + virtual size_t size()const = 0; + + /** + * Returns the minimal polynomial that vanishes over the set + */ + virtual const UnivariatePolynomialInterface& vanishingPoly()const = 0; + + /** + * Returns whether a field element is a member of the set + */ + virtual bool contains(const FieldElement& e)const = 0; + + virtual ~FiniteSetInterface(){}; +}; + +} //namespace Algebra + +#endif // __SET_INTERFACE_H diff --git a/libstark/src/common/Algebra/LinearSpace.cpp b/libstark/src/common/Algebra/LinearSpace.cpp new file mode 100644 index 0000000..31fb9d4 --- /dev/null +++ b/libstark/src/common/Algebra/LinearSpace.cpp @@ -0,0 +1,59 @@ +#include "LinearSpace.hpp" +#include "algebraLib/UnivariatePolynomialGeneral.hpp" +#include "algebraLib/SubspacePolynomial.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include + +namespace Algebra{ + +LinearSpace::LinearSpace(const std::vector& orderedBasis, const FieldElement affineShift) : + orderedBasis_(orderedBasis), + affineShift_(affineShift), + subspacePoly_(elementsSet_t(orderedBasis.begin(),orderedBasis.end())) + {}; + +bool LinearSpace::exist(const std::unique_ptr& pred)const { + + const size_t spaceSize = Infrastructure::POW2(orderedBasis_.size()); + bool isFound = false; +#ifndef _DEBUG +#pragma omp parallel +#endif + { + const size_t numThreads = omp_get_num_threads(); + const size_t currThreadId = omp_get_thread_num(); + for (size_t i = 0; ((i+currThreadId)< spaceSize) && (!isFound); i+=numThreads){ + const auto currLocation = i+currThreadId; + FieldElement e = getSpaceElementByIndex(orderedBasis_,affineShift_, currLocation); + if (pred->test(e) == true){ + isFound=true; + } + +#ifdef PRINT_PROGRESS + if (currLocation%100 == 0){ + std::cout<& orderedBasis, const FieldElement affineShift = zero()); + + /** + * @brief Checks if an elements that satisfies a predicate exist in the set + * @param pred the predicate + * @return True iff such an element exists + */ + bool exist(const std::unique_ptr& pred)const; + size_t size()const; + const UnivariatePolynomialInterface& vanishingPoly()const; + + /** + * Returns whether a field element is a member of the set + */ + bool contains(const FieldElement& e)const; + +private: + const std::vector orderedBasis_; + const FieldElement affineShift_; + const SubspacePolynomial subspacePoly_; +}; + +} // namespace Algebra + +#endif // ALGEBRA_LINEARSPACE_HPP__ diff --git a/libstark/src/common/Algebra/MultiVarPoly.cpp b/libstark/src/common/Algebra/MultiVarPoly.cpp new file mode 100644 index 0000000..41fac3a --- /dev/null +++ b/libstark/src/common/Algebra/MultiVarPoly.cpp @@ -0,0 +1,100 @@ +//class constraintPoly : public PolynomialInterface{ +//public: +// +// constraintPoly(const Polynomial& poly, const vector& varsTranslation){ +// const FieldElement ZERO = generateConstant(); +// if (poly.getConstant() != ZERO){ +// product_t constantSummand; +// constantSummand.coeff = poly.getConstant(); +// poly_.push_back(constantSummand); +// } +// +// for (const auto& monomial : poly.getMonomials()){ +// if (monomial.getCoefficient() == ZERO)continue; +// +// product_t newProd; +// newProd.coeff = monomial.getCoefficient(); +// +// for (size_t varId = 0; varId < varsTranslation.size(); varId++){ +// for (size_t i = 0; i< monomial.getVariableDegree(varsTranslation[varId]); i++){ +// newProd.vars.push_back(varId); +// } +// } +// +// poly_.push_back(newProd); +// } +// } +// +// FieldElement eval(const vector& x)const{ +// const FieldElement ZERO = generateConstant(); +// const FieldElement ONE = generateConstant(); +// +// FieldElement res = ZERO; +// +// for (const auto& prod : poly_){ +// FieldElement prod_res = prod.coeff; +// for (const auto& varId : prod.vars){ +// prod_res *= x[varId]; +// } +// +// res += prod_res; +// } +// +// return res; +// } +// +// FieldElement eval(const sparceAssignment_t& x)const{ +// const FieldElement ZERO = generateConstant(); +// const FieldElement ONE = generateConstant(); +// +// FieldElement res = ZERO; +// +// for (const auto& prod : poly_){ +// FieldElement prod_res = prod.coeff; +// for (const auto& varId : prod.vars){ +// prod_res *= x.at(varId); +// } +// +// res += prod_res; +// } +// +// return res; +// } +// +// PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ +// PolynomialDegree deg(-1); +// +// for (const auto& prod : poly_){ +// PolynomialDegree prodDeg(0); +// for (const auto& varId : prod.vars){ +// prodDeg = prodDeg + inputDegrees[varId];//ARIEL - fixed + to = +// } +// +// deg = max(deg, prodDeg); +// } +// +// return deg; +// } +// +// unique_ptr clone()const{ +// return unique_ptr(new constraintPoly(*this)); +// } +// +// bool isEffectiveInput(const size_t varId)const{ +// +// for (const auto& prod : poly_){ +// for (const auto& varId_ : prod.vars){ +// if (varId_ == varId)return true; +// } +// } +// return false; +// } +// +//private: +// typedef size_t varId_t; +// typedef struct product_t{ +// vector vars; +// FieldElement coeff; +// }; +// vector poly_; +//}; diff --git a/libstark/src/common/Algebra/MultiVarPoly.hpp b/libstark/src/common/Algebra/MultiVarPoly.hpp new file mode 100644 index 0000000..a54aa74 --- /dev/null +++ b/libstark/src/common/Algebra/MultiVarPoly.hpp @@ -0,0 +1,102 @@ +/**MultiVarPoly.hpp - simple implementation of Multivariate Polynomial as a vector of monomials*/ +#include +#include +#include +using namespace std; +namespace Algebra{ + struct MultiVarMonomial + { + FieldElement coeff; + vector vars; + }; + + class MultiVarPoly : public PolynomialInterface{ +public: + + //generates the zero polynomial + MultiVarPoly() :monomials(){} + //adds monomial to polynomial. If monomial exists with different coefficient adds the new coefficint to exisiting monomial + //Warning:Assumes monomial always contains variables in same order. + void AddMonomial(MultiVarMonomial M){ + for (auto it = monomials.begin(); it != monomials.end(); it++){ + if ((*it).vars == M.vars){ + (*it).coeff += M.coeff; + return; + } + } + monomials.push_back(M); + } + + //adds monomial to polynomial with coeff 1, assuming this monomial does not yet appear. If monomial already exists does nothing. + void AddMonomial(vector M){ + for (auto it = monomials.begin(); it != monomials.end(); it++){ + if ((*it).vars == M) + return; + + } + MultiVarMonomial A = {one(), M}; + monomials.push_back(A); + } + FieldElement eval(const vector& x)const{ + + FieldElement res = zero(); + + for (const auto& prod : monomials){ + FieldElement prod_res = prod.coeff; + for (const auto& varId : prod.vars){ + prod_res *= x[varId]; + } + + res += prod_res; + } + + return res; + } + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + PolynomialDegree deg(-1); + + for (const auto& prod : monomials){ + PolynomialDegree prodDeg(0); + for (const auto& varId : prod.vars){ + prodDeg = degreeOfProduct(prodDeg,inputDegrees[varId]); + } + + deg = max(deg, prodDeg); + } + + return deg; + } + + int getNonNegativeDegree()const{ + int deg = 0; + for (const auto& prod : monomials){ + int prodDeg = 0; + for (const auto& varId : prod.vars){ + prodDeg = prodDeg + 1; + } + + deg = max(deg, prodDeg); + } + + return deg; + } + + unique_ptr clone()const{ + return unique_ptr(new MultiVarPoly(*this)); + } + + bool isEffectiveInput(const size_t varId)const{ + + for (const auto& prod : monomials){ + for (const auto& varId_ : prod.vars){ + if (varId_ == varId)return true; + } + } + return false; + } + +private: + vector monomials; + }; +} diff --git a/libstark/src/common/Algebra/MultiVarPolyIddo.hpp b/libstark/src/common/Algebra/MultiVarPolyIddo.hpp new file mode 100644 index 0000000..e541b4d --- /dev/null +++ b/libstark/src/common/Algebra/MultiVarPolyIddo.hpp @@ -0,0 +1,121 @@ +/**MultiVarPoly.hpp - simple implementation of Multivariate Polynomial as a vector of monomials*/ +#include "FieldElement.hpp" +#include "PolynomialDegree.hpp" +#include "PolynomialInterface.hpp" +using namespace std; +namespace Algebra{ + struct MultiVarMonomial + { + FieldElement coeff; + vector> vars; + }; + + class MultiVarPoly : public PolynomialInterface{ + public: + + //adds monomial to polynomial. If monomial exists with different coefficient adds the new coefficint to exisiting monomial + //Warning:Assumes monomial always contains variables in same order. + void AddMonomial(MultiVarMonomial M){ + for (auto it = monomials.begin(); it != monomials.end(); it++){ + if ((*it).vars == M.vars){ + (*it).coeff += M.coeff; + return; + } + } + monomials.push_back(M); + } + + //adds monomial to polynomial with coeff 1, assuming this monomial does not yet appear. If monomial already exists does nothing. + void AddMonomial(vector> M){ + for (auto it = monomials.begin(); it != monomials.end(); it++){ + if ((*it).vars == M) + return; + + } + MultiVarMonomial A = { one(), M }; + monomials.push_back(A); + } + FieldElement eval(const vector& x)const{ + const FieldElement ZERO = generateConstant(); + const FieldElement ONE = generateConstant(); + + FieldElement res = ZERO; + + for (const auto& prod : monomials){ + FieldElement prod_res = prod.coeff; + for (const auto& varId : prod.vars){ + for (long i = 0; i(); + // const FieldElement ONE = generateConstant(); + + // FieldElement res = ZERO; + + // for (const auto& prod : monomials){ + // FieldElement prod_res = prod.coeff; + // for (const auto& varId : prod.vars){ + // prod_res *= x.at(varId); + // } + + // res += prod_res; + // } + + // return res; + //} + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + PolynomialDegree deg(-1); + + for (const auto& prod : monomials){ + PolynomialDegree prodDeg(0); + for (const auto& varId : prod.vars){ + prodDeg = prodDeg + PolynomialDegree(varId.second)*inputDegrees[varId.first]; + } + + deg = max(deg, prodDeg); + } + + return deg; + } + + int getNonNegativeDegree()const{ + int deg; + for (const auto& prod : monomials){ + int prodDeg = 0; + for (const auto& varId : prod.vars){ + prodDeg = prodDeg + varId.second; + } + + deg = max(deg, prodDeg); + } + + return deg; + } + + unique_ptr clone()const{ + return unique_ptr(new MultiVarPoly(*this)); + } + + bool isEffectiveInput(const size_t varId)const{ + + for (const auto& prod : monomials){ + for (const auto& varId_ : prod.vars){ + if (varId_.first == varId)return true; + } + } + return false; + } + + private: + vector monomials; + }; +} diff --git a/libstark/src/common/Algebra/ShiftedSubspacePolynomial.cpp b/libstark/src/common/Algebra/ShiftedSubspacePolynomial.cpp new file mode 100644 index 0000000..d6995cd --- /dev/null +++ b/libstark/src/common/Algebra/ShiftedSubspacePolynomial.cpp @@ -0,0 +1,94 @@ + +/******************************************************************************************************/ +/************************************ ShiftedSubspacePolynomial Class ***************************************/ +/******************************************************************************************************/ + +#include "ShiftedSubspacePolynomial.hpp" +using std::vector; + +namespace Algebra{ + FieldElement square(FieldElement x){ + return x*x; + } + + /** Class Constructor - Computes the subspace polynomial that vanishes exactly over the span of spanSet*/ + + ShiftedSubspacePolynomial::ShiftedSubspacePolynomial(const elementsSet_t& spanSet, const FieldElement& constantFactor):AffinePolynomial(computeVanishingPoly(spanSet), constantFactor){ + + } + + + /** Returns the vector of coefficients of the subspace polynomial vanishing on the span of spanSet={e1,..,ek} + * For simplicity, let us first describe an alg that would work assuming {e1,..ek} are lin. independent: + * The algorithm would inductively computes the (coeffs of the) subspace polynomial P_i of the subspace spanned by {e_1,..,e_i}. + * For i=1, this is the polynomial x^2 + e1*x. Assume we have computed P_{i-1}. + * it turns out that P_i is P_{i-1} composed from the outside with x^2 + P_{i-1}(e_i)*x + * this is the formula the code implements. + All this was assuming {e1,..,ek} were linearly independent. + Now, to remove this assumption, before constructing the next P_i we check if the current e_i is linearly dependent + on {e1,..,e_{i-1}} - we do this by simply checking if P_{i-1}(e_i) =0. + If so, we simply let P_i= P_{i-1}. + Otherwise, we derive P_i using the formula described above. + */ + vector ShiftedSubspacePolynomial::computeVanishingPoly(const elementsSet_t& spanSet) { + + vector coefficients ; + //if spanSet is empty we return the zero polynomial + if (spanSet.size() == 0) + return coefficients; + + + //Initialzing as the subspace poly of the space {0} - which is x + coefficients.push_back(one()); + FieldElement c, eiPower; + unsigned int i = 0, j; + elementsSet_t::iterator z = spanSet.begin(); + while (z != spanSet.end()){ + //compute c= P_{i-1}(e_i) + c = zero(); + + eiPower = *z; //eiPower starts as ei + for (j = 0; j < coefficients.size(); j++) { + c += coefficients[j] * eiPower; + eiPower = square(eiPower); + } + //Now check if c is non-zero - which means e_i is independent of previous ej's. + if (!(c == zero())){ + //if so, update P_i to be zero on the extended span of {e1,..ei} + i++; + coefficients.push_back(one()); + for (j = i - 1; j >= 1; j--) { + coefficients[j] = c*coefficients[j] + square(coefficients[j - 1]); + } + coefficients[0] = c*coefficients[0]; + + } + z++; + } + return coefficients; + } + + + + + + /** Testing purposes */ + //void SubspacePolynomial::print() const { + // cout << "Vanishing Polynomial: " << endl; + // + // if (coefficients == NULL) { + // cout << "Empty object!" << endl; + // return; + // } + // + // for (unsigned long i = 0; i(0)) + // cout << "Constant Factor: " << constantFactor << endl; + //} + + + +}//namespace Algebra diff --git a/libstark/src/common/Algebra/ShiftedSubspacePolynomial.hpp b/libstark/src/common/Algebra/ShiftedSubspacePolynomial.hpp new file mode 100644 index 0000000..0be8e96 --- /dev/null +++ b/libstark/src/common/Algebra/ShiftedSubspacePolynomial.hpp @@ -0,0 +1,27 @@ +/** +* A class for special affine polynomials - that we here call `shifted subspace polynomials'. +These have the property that the linear part is a subspace polynomial - (see explanation in the class +SubspacePolynomial) +The only difference between this class and SubspacePolynomial is that the constantFactor_ member +is not required to be zero. +(and therefore, the constructor of this class recieves the value of constantFactor_ as a parameter) +*/ + +#ifndef _COMMON_ALGEBRA_SHIFTEDSUBSPACEPOLYNOMIAL_HPP__ +#define _COMMON_ALGEBRA_SHIFTEDSUBSPACEPOLYNOMIAL_HPP__ + +#include "algebraLib/AffinePolynomial.hpp" +namespace Algebra +{ + + class ShiftedSubspacePolynomial : public AffinePolynomial{ + public: + ShiftedSubspacePolynomial(const elementsSet_t& spanSet, const FieldElement& constantFactor); + + private: + static std::vector computeVanishingPoly(const elementsSet_t& spanSet); + }; +}// namespace Algebra + +#endif // !_COMMON_ALGEBRA_SHIFTEDSUBSPACEPOLYNOMIAL_HPP__ + diff --git a/libstark/src/common/Infrastructure/Infrastructure.cpp b/libstark/src/common/Infrastructure/Infrastructure.cpp new file mode 100644 index 0000000..90d75f4 --- /dev/null +++ b/libstark/src/common/Infrastructure/Infrastructure.cpp @@ -0,0 +1,28 @@ +#include "Infrastructure.hpp" + +namespace Infrastructure { + +/*****************************************************************************/ +/**************************** Basic Math ***********************************/ +/*****************************************************************************/ + +///Returns log in base 2 +double Log2( double n ) { + return log(n) / log((double)2); // log(n)/log(2) is ::Infrastructure::Log2. +} + +/// Returns an upper bound on log2(i). Namely, returns the number of binary digits needed to store +/// the value 'i'. When i == 0 returns 0. +unsigned int Log2ceil(uint64_t i) { + int retval = i ? 1 : 0 ; + while (i >>= 1) {++retval;} + return retval; +} + +///Returns true iff x is a power of 2 +bool IsPower2(const long x) { + return ( (x > 0) && ((x & (x - 1)) == 0) ); +} + +} // namespace Infrastructure + diff --git a/libstark/src/common/Infrastructure/Infrastructure.hpp b/libstark/src/common/Infrastructure/Infrastructure.hpp new file mode 100644 index 0000000..6bae8fc --- /dev/null +++ b/libstark/src/common/Infrastructure/Infrastructure.hpp @@ -0,0 +1,36 @@ +#ifndef __Infrastructure_HPP +#define __Infrastructure_HPP + +#include +#include + +namespace Infrastructure { + +/********************************************************/ +/****************** Basic Math **************************/ +/********************************************************/ + +//Calculates ::Infrastructure::Log2 of a number. +double Log2(double n); + +//Calculates upper bound of Log2 of a number (number of bits needed to represent value) +unsigned int Log2ceil(uint64_t i); + +//Returns true iff the given number is a power of 2. +bool IsPower2(const long x); + +// Returns 2^exponent +/*constexpr*/ inline unsigned long POW2(int exponent) { + //assert(exponent>=0); + return (1UL) << exponent; +} + +//Returns the ceiling of a when a is of type double. +/*constexpr*/ inline int64_t CEIL(double a) { + return (int64_t)ceil(a); +} +//#define CEIL(a) ((int64_t)ceil((double)(a))) + +} // namespace Infrastructure + +#endif // __Infrastructure_HPP diff --git a/libstark/src/common/Utils/ErrorHandling.cpp b/libstark/src/common/Utils/ErrorHandling.cpp new file mode 100644 index 0000000..bce77a5 --- /dev/null +++ b/libstark/src/common/Utils/ErrorHandling.cpp @@ -0,0 +1,69 @@ +/********************************************** ErrorHandling.cpp ************************************************/ +/** + * @file. + * + * The file ErrorHandling.cpp contains various utility functions and date type definitions used by the different + * classes in the project. + * + * For more information - Read the documentation of the header file ErrorHandling.hpp. + */ + /************************************************************************************************************/ +#include +#include +#include "ErrorHandling.hpp" + +using namespace std; + +namespace libstark { + + + + +/*****************************************************************************/ +/*********************** ErrorHandling********** ****************************/ +/*****************************************************************************/ + +void ErrorHandling::fatalError(string msg) { + cerr << "ERROR: " << msg << endl << endl; + printStacktrace(); + throw std::runtime_error(msg); +} + +void ErrorHandling::fatalError(const stringstream& msg) { + fatalError(msg.str()); +} + +void ErrorHandling::warning(string msg) { + cerr << "WARNING: " << msg << endl << endl; + printStacktrace(); +} + +void ErrorHandling::warning(const stringstream& msg) { + warning(msg.str()); +} + +void ErrorHandling::info(string msg) { + cerr << msg << endl << endl; +} + +void ErrorHandling::info(const stringstream& msg) { + info(msg.str()); +} + +void ErrorHandling::printStacktrace() { +#ifdef __GNUC__ + cerr << "Stack trace (pipe through c++filt to demangle identifiers):" << endl; + const int maxFrames = 100; + void* frames[maxFrames]; + // Fill array with pointers to stack frames + int numFrames = backtrace(frames, maxFrames); + // Decode frames and print them to stderr + backtrace_symbols_fd(frames, numFrames, STDERR_FILENO); +#else + //TODO make this available for Windows + cerr << " (stack trace not available on this platform)" << endl; +#endif // __GNUC__ +} + + +} // of namespace diff --git a/libstark/src/common/Utils/ErrorHandling.hpp b/libstark/src/common/Utils/ErrorHandling.hpp new file mode 100644 index 0000000..a96b5b8 --- /dev/null +++ b/libstark/src/common/Utils/ErrorHandling.hpp @@ -0,0 +1,60 @@ +#ifndef _COMMON_UTILS_HPP_ +#define _COMMON_UTILS_HPP_ + +#include +#include + +#ifdef __GLIBC__ +#include // backtraces +#endif + +namespace libstark { + +/********************************************************/ +/******************* Error Handling *********************/ +/********************************************************/ + +// declare a function as never returning, to quiet down "control reaches end of non-void function" warnings +#if defined(_MSC_VER) // VisualC++ + #define __noreturn _declspec(noreturn) +#elif defined(__GNUC__) + #define __noreturn __attribute__((noreturn)) +#else + #define __noreturn +#endif + + + +/** + * The ErrorHandling class containimplements the functionality of displaying the content of error + * messages (including content of call stack when error happened), and exiting the program. + */ +class ErrorHandling { +public: + static void __noreturn fatalError(std::string msg); + static void __noreturn fatalError(const std::stringstream& msg); + static void warning(std::string msg); + static void warning(const std::stringstream& msg); + static void info(std::string msg); + static void info(const std::stringstream& msg); + static void printStacktrace(); + +}; + +#define _COMMON_DEBUG_MSG(msg) do { \ + std::cerr << msg << " (In file " << __FILE__ << " line " << __LINE__ << ".)"; \ + } while (0) + + +#define _COMMON_FATAL(msg) do { \ + std::stringstream msgStream; \ + msgStream << msg << " (In file " << __FILE__ << " line " << __LINE__ << ".)"; \ + libstark::ErrorHandling::fatalError(msgStream.str()); \ + } while (0) + +// TODO change _COMMON_ASSERT to not run in debug +#define _COMMON_ASSERT(predicate, msg) if(!(bool(predicate))) _COMMON_FATAL(msg); + +} // of namespace + +#endif //_COMMON_UTILS_HPP_ diff --git a/libstark/src/common/Utils/TaskReporting.hpp b/libstark/src/common/Utils/TaskReporting.hpp new file mode 100644 index 0000000..32f3563 --- /dev/null +++ b/libstark/src/common/Utils/TaskReporting.hpp @@ -0,0 +1,6 @@ +#ifndef __TaskReporting_HPP +#define __TaskReporting_HPP + +#define TASK(_name) {}; + +#endif // __TaskReporting_HPP diff --git a/libstark/src/common/Utils/Timing.cpp b/libstark/src/common/Utils/Timing.cpp new file mode 100644 index 0000000..f0fd87d --- /dev/null +++ b/libstark/src/common/Utils/Timing.cpp @@ -0,0 +1,30 @@ +#include "Timing.hpp" + +namespace libstark { + +/********************************************************/ +/******************* Timing **********************/ +/********************************************************/ + +Timer::Timer() { +#ifdef __linux__ + clock_gettime(CLOCK_MONOTONIC, &startTimeSpec); +#else + startTime = clock(); // wallclock on Windows, process time (total over threads) on Linux +#endif +} + +double Timer::getElapsed() { +#ifdef __linux__ + struct timespec endTimeSpec; + clock_gettime(CLOCK_MONOTONIC, &endTimeSpec); + double elapsed = (endTimeSpec.tv_sec - startTimeSpec.tv_sec); + elapsed += (endTimeSpec.tv_nsec - startTimeSpec.tv_nsec) / 1000000000.0; + return elapsed; +#else + clock_t endTime = clock(); + return (double(endTime) - double(startTime)) / CLOCKS_PER_SEC; +#endif +} + +} // namespace libstark diff --git a/libstark/src/common/Utils/Timing.hpp b/libstark/src/common/Utils/Timing.hpp new file mode 100644 index 0000000..fb147a4 --- /dev/null +++ b/libstark/src/common/Utils/Timing.hpp @@ -0,0 +1,31 @@ +#ifndef __Timing_HPP +#define __Timing_HPP + +#include + +namespace libstark { + +/********************************************************/ +/******************* Timing **********************/ +/********************************************************/ + +/** + * A lightweight class for elapsed-time measurements + */ +class Timer { + private: +#ifdef __linux__ + struct timespec startTimeSpec; +#else + clock_t startTime; +#endif + public: + Timer(); + + /** Elapsed time in seconds */ + double getElapsed(); +}; + +} // namespace libstark + +#endif // __Timing_HPP diff --git a/libstark/src/common/Utils/specsPrint.cpp b/libstark/src/common/Utils/specsPrint.cpp new file mode 100644 index 0000000..d226853 --- /dev/null +++ b/libstark/src/common/Utils/specsPrint.cpp @@ -0,0 +1,41 @@ +#include "specsPrint.hpp" +#include +#include + +namespace libstark{ + + using std::vector; + using std::string; + using std::pair; + using std::max; + + specsPrinter::specsPrinter(const string title):title_(title),data_(0){}; + void specsPrinter::addLine(const string name, const string val){ + data_.push_back(pair(name,val)); + } + + void specsPrinter::print()const{ + using std::cout; + using std::endl; + using std::setw; + using std::setfill; + + int namesLen=0, valsLen = 0; + for (const auto& p : data_){ + namesLen = max(namesLen,int(p.first.length())); + valsLen = max(valsLen,int(p.second.length())); + } + + const int len = max(int(title_.length()), namesLen + valsLen + 3); + valsLen = len - namesLen - 3; + + cout< +#include +#include + +#ifndef __SPECS_PRINT_HPP__ +#define __SPECS_PRINT_HPP__ + +namespace libstark{ + + class specsPrinter{ + public: + specsPrinter(const std::string title); + void addLine(const std::string name, const std::string val); + void print()const; + + private: + std::string title_; + std::vector> data_; + }; + +} // namespace libstark + +#endif //#ifndef __SPECS_PRINT_HPP__ diff --git a/libstark/src/common/langCommon/Sequence.hpp b/libstark/src/common/langCommon/Sequence.hpp new file mode 100644 index 0000000..e38c941 --- /dev/null +++ b/libstark/src/common/langCommon/Sequence.hpp @@ -0,0 +1,39 @@ +#ifndef __Sequence_HPP +#define __Sequence_HPP + +#include + +namespace libstark { + +/** + * @class Sequence + * @brief An interface class for sequences for some type T + */ +template +class Sequence { +public: + typedef uint64_t index_t; + /** + * @brief A mapping of integers to T + * @param index an integer + * @return T element + */ + virtual T getElementByIndex(index_t index)const = 0; + + virtual ~Sequence(){}; +}; + +/** + * @class LazyVector + * @brief An interface class for lazy evaluation vector for some type T + */ +template +class LazyVector : public Sequence { + public: + virtual typename Sequence::index_t size()const = 0; +}; + + +} // namespace libstark + +#endif // __Sequence_HPP diff --git a/libstark/src/languages/Acsp/AcspInstance.hpp b/libstark/src/languages/Acsp/AcspInstance.hpp new file mode 100644 index 0000000..64a74cc --- /dev/null +++ b/libstark/src/languages/Acsp/AcspInstance.hpp @@ -0,0 +1,240 @@ +/** + * @file AcspInstance.hpp + * @brief Header file for Acsp Partial Instance + * + * Contains Acsp Partiial instance definitions + * + * @author Michael Riabzev, RiabzevMichael@gmail.com + * ===================================================================================== + */ +#pragma once + +#ifndef __Acsp_Instance_HPP +#define __Acsp_Instance_HPP + +#include "AcspWitness.hpp" +#include "common/langCommon/Sequence.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include +#include +#include "common/Algebra/FiniteSetInterface.hpp" +#include +#include +#include +#include + +namespace libstark { + +/** + * @class AcspInstance + * @brief class for Acsp Instance + * + * An Acsp partial instance is a tuple \f$(\mathbb{F},H,\vec{N},P,WitnessDegreeBound,B)\f$ such that: \n + * \f$\mathbb{F}\f$ is a field + * + * \f$H \subset \mathbb{F}\f$ is a subset of \f$ \mathbb{F} \f$ + * + * \f$\vec{N} \in \mathbb{F}[x]^k\f$ is a vector of univariate polynomials over \f$ \mathbb{F} \f$ + * + * \f$P \in \mathbb{F}[x_0,x_1,x_2,\dots,x_k]\f$ is a multivariate polynomial over \f$ \mathbb{F} \f$ + * + * \f$WitnessDegreeBound \in \mathbb{N}\f$ is an apper bound for satisfying witness + * + * \f$B \subset \mathbb{F} \times \amthbb{F}\f$ is the boundry constraints set + * + * + * For a polynomial \f$Q \in \mathbb{F}[x]\f$ we define \f$P \circ (x \vert Q \circ \vec{N}) \in \mathbb{F}[x]\f$ by + * \f$(P \circ (x \vert Q \circ \vec{N}))(x) := P(x,Q(N_1(x)),Q(N_2(x)),\dots,Q(N_k(x)))\f$ + * + * An Acsp partial instance \f$(\mathbb{F},H,\vec{N},P,\lambda,I)\f$ is satisfiable partial instance if and only if + * there exists \f$A \in \mathbb{F}[x]\f$ and \f$ \vec{x} \in \mathbb{F}^n \f$ (for some \f$ n \in \mathbb{N} \f$) such that: + * \f{eqnarray*}{ + * \forall z \in H: (P \circ (x \vert A \circ \vec{N}))(z) = 0 \\ + * \deg A <= WitnessDegreeBound \\ + * \forall (x,y) \in B : A(x)=y \\ + * \f} + * + * In the code we give more descriptive names: + * + * we name \f$ \mathbb{F} \f$ as 'contextField' \n + * we name \f$H\f$ as 'vanishingSet' \n + * we name \f$N\f$ as 'neighborPolys' \n + * we name \f$P\f$ as 'constraintPoly' \n + * we name \f$B\f$ as 'boundaryConstraints' \n + * + * + * Methods:\n + * Instance class contains only getters, + * constructor and a destructor. + */ + +class AcspInstance { +public: + typedef Algebra::FiniteSetInterface set; + typedef Algebra::UnivariatePolynomialInterface uniPoly; + typedef std::vector> polynomialsVec; + typedef Algebra::PolynomialInterface polynomial; + typedef Algebra::FieldElement fieldElement; + typedef std::map boundaryConstraints_t; + typedef std::function< std::vector(const AcspInstance&, const AcspWitness&, const std::vector& basis, const Algebra::FieldElement& shift, const bool witnessIsEvaluation)> compAlgorithm_t; + typedef std::function< std::vector>(const AcspInstance&, const AcspWitness&)> witnessCheckerHelperAlgorithm_t; + + AcspInstance( + std::unique_ptr&& vanishingSet, + polynomialsVec&& neighborPolys, + std::unique_ptr&& constraintPoly, + const Algebra::PolynomialDegree& witnessDegreeBound, + const boundaryConstraints_t& boundaryConstraints, + const compAlgorithm_t& compositionAlgorithm = naiveComposition_N_division_Alg, + const witnessCheckerHelperAlgorithm_t& witnessCheckerHelperAlgorithm = naiveWCHelper + ) + : + vanishingSet_(std::move(vanishingSet)), + neighborPolys_(0), + constraintPoly_(std::move(constraintPoly)), + witnessDegreeBound_({witnessDegreeBound}), + boundaryConstraints_({boundaryConstraints}), + compositionAlgorithm_(compositionAlgorithm), + witnessCheckerHelperAlgorithm_(witnessCheckerHelperAlgorithm) + { + neighborPolys_.push_back(std::move(neighborPolys)); + } + + AcspInstance( + std::unique_ptr&& vanishingSet, + std::vector&& neighborPolys, + std::unique_ptr&& constraintPoly, + const std::vector& witnessDegreeBound, + const std::vector& boundaryConstraints, + const compAlgorithm_t& compositionAlgorithm = naiveComposition_N_division_Alg, + const witnessCheckerHelperAlgorithm_t& witnessCheckerHelperAlgorithm = naiveWCHelper + ) + : + vanishingSet_(std::move(vanishingSet)), + neighborPolys_(std::move(neighborPolys)), + constraintPoly_(std::move(constraintPoly)), + witnessDegreeBound_(witnessDegreeBound), + boundaryConstraints_(boundaryConstraints), + compositionAlgorithm_(compositionAlgorithm), + witnessCheckerHelperAlgorithm_(witnessCheckerHelperAlgorithm) + {}; + + AcspInstance(AcspInstance&& src) = default; + + AcspInstance() = delete; + AcspInstance(const AcspInstance& src) = delete; + + inline const set& vanishingSet()const { + return *(vanishingSet_.get()); + } + + inline const std::vector& neighborPolys()const { + return neighborPolys_; + } + + inline const polynomial& constraintPoly()const { + return *(constraintPoly_.get()); + } + + inline const std::vector& boundaryConstraints()const{ + return boundaryConstraints_; + } + + inline const std::vector witnessDegreeBound()const{ + return witnessDegreeBound_; + } + + inline std::vector composeWithWitness_and_divideByVanishingSpacePoly(const AcspWitness& witness, const std::vector& basis, const Algebra::FieldElement& shift, const bool witnessIsEvaluation = false)const{ + return compositionAlgorithm_(*this,witness,basis,shift,witnessIsEvaluation); + } + + inline std::vector> prepareForWitnessChecker(const AcspWitness& witness)const{ + return witnessCheckerHelperAlgorithm_(*this,witness); + } + + private: + + std::unique_ptr vanishingSet_; + std::vector neighborPolys_; + std::unique_ptr constraintPoly_; + std::vector witnessDegreeBound_; + std::vector boundaryConstraints_; + + //A hint for fast construction of the composition polynomial + compAlgorithm_t compositionAlgorithm_; + + //A hint for the determenistic witness checker for faster check if vanishing on vanishing set + witnessCheckerHelperAlgorithm_t witnessCheckerHelperAlgorithm_; + + static std::vector naiveComposition_N_division_Alg(const AcspInstance& instance, const AcspWitness& witness, const std::vector& basis, const Algebra::FieldElement& shift, const bool witnessIsEvaluation){ + using std::vector; + using std::min; + using std::unique_ptr; + using Algebra::FieldElement; + using Algebra::PolynomialDegree; + using Algebra::zero; + using Algebra::UnivariatePolynomialGeneral; + + // + //get the composition degree bound + // + vector constraintsInputDegrees; + + // first input is "x" which has degree 1 + constraintsInputDegrees.push_back(PolynomialDegree(1)); + + // rest are composition of neighbor with witness + for(size_t wIndex = 0; wIndex < witness.assignmentPolys().size(); wIndex++){ + const auto witnessDegree = witness.assignmentPolys()[wIndex]->getDegree(); + for (const auto& n : instance.neighborPolys()[wIndex]){ + constraintsInputDegrees.push_back(n->getDegreeBound(witnessDegree)); + } + } + + // get the composition degree bound + const PolynomialDegree degBound = instance.constraintPoly().getDegreeBound(constraintsInputDegrees); + + // + //define interpolation space + // + const size_t degForPoly = degBound.isInteger()? ceil(log2(1+PolynomialDegree::integral_t(degBound))) : 0; + const auto interpolationBasis = Algebra::getStandartOrderedBasis(min(Algebra::ExtensionDegree, int(degForPoly))); + + //construct evaluation + vector evaluation(Infrastructure::POW2(interpolationBasis.size())); + for(size_t i=0; i< evaluation.size(); i++){ + const FieldElement x = getSpaceElementByIndex(interpolationBasis,zero(),i); + + //construct the assignment for the constraints poly + vector assignment; + assignment.push_back(x); + for(size_t wIndex = 0; wIndex < witness.assignmentPolys().size(); wIndex++){ + for(const auto& n : instance.neighborPolys()[wIndex]){ + assignment.push_back(witness.assignmentPolys()[wIndex]->eval(n->eval(x))); + } + } + + //evaluate and return + evaluation[i] = instance.constraintPoly().eval(assignment); + } + + UnivariatePolynomialGeneral compositionPoly(evaluation,interpolationBasis,zero()); + + //build denominator and divide + return (UnivariatePolynomialGeneral(instance.vanishingSet().vanishingPoly()).divideByMe(compositionPoly))->eval(basis,shift); + } + + static std::vector> naiveWCHelper(const AcspInstance& instance, const AcspWitness& witness){ + std::vector> res; + for(size_t wIndex = 0; wIndex < witness.assignmentPolys().size(); wIndex++){ + res.push_back(std::unique_ptr(new Algebra::UnivariatePolynomialGeneral(*(witness.assignmentPolys()[wIndex])))); + } + + return res; + } + +}; + +}// namespace libstark + +#endif // __Acsp_Instance_HPP diff --git a/libstark/src/languages/Acsp/AcspWitness.hpp b/libstark/src/languages/Acsp/AcspWitness.hpp new file mode 100644 index 0000000..f972ad5 --- /dev/null +++ b/libstark/src/languages/Acsp/AcspWitness.hpp @@ -0,0 +1,74 @@ +/** + * @file AcspWitness.hpp + * @brief Header file for Acsp witness + * + * @author Michael Riabzev, RiabzevMichael@gmail.com + * ===================================================================================== + */ +#pragma once // This is not needed, here just to get rid of an annoying VS2010 warning. +#ifndef __Acsp_WITNESS_HPP +#define __Acsp_WITNESS_HPP + +#include + +#include + +namespace libstark { + +/** + * @class AcspWitness + * @brief class for Acsp witness + * + * This class describes a witness for AcspFullInstance. + * A witness is a polynomial \f$ A \in \mathbb{F}[x] \f$, + * and named Assignment Polynomial. + * Such a polynomial shows that\n + * \f$(\mathbb{F},H,\vec{N},P,witnessDegreeBound,B)\f$ is a satisfiable AcspInstance + * if and only if + * \f{eqnarray*}{ + * \forall z \in H: (P \circ (x \vert A \circ \vec{N}))(z) = 0 \\ + * \deg A <= witnessDegreeBound \\ + * \lambda \cdot \deg(P \circ (x \vert A \circ \vec{N})) \le |\mathbb{F}| \\ + * \forall (x,y) \in B : A(x)=y \\ + * \f} + * + * In the code we name the assignment polynomial \f$A\f$ + * simply 'assignmentPoly' + * + * Methods: + * Witness class contains only getters, + * an empty constructor and a destructor. + * The only possible way to change its data members + * is using the reduction class from EGCP or inside a UTEST. + */ +class AcspWitness { +public: + typedef Algebra::UnivariatePolynomialInterface polynomial; + typedef std::unique_ptr poly_ptr; + typedef std::vector poly_vec; + + AcspWitness(poly_ptr&& assignmentPoly):assignmentPolys_(1){ + assignmentPolys_[0] = std::move(assignmentPoly); + } + + AcspWitness(poly_vec&& assignmentPolys):assignmentPolys_(std::move(assignmentPolys)){}; + + AcspWitness(AcspWitness&& src) = default; + AcspWitness(const AcspWitness& src) = delete; + + inline const polynomial& assignmentPoly() const { + return *(assignmentPolys_[0]); + } + + inline const poly_vec& assignmentPolys() const { + return assignmentPolys_; + } + +private: + + poly_vec assignmentPolys_; +}; + +} //namespace libstark + +#endif //__Acsp_WITNESS_HPP diff --git a/libstark/src/languages/Acsp/AcspWitnessChecker.cpp b/libstark/src/languages/Acsp/AcspWitnessChecker.cpp new file mode 100644 index 0000000..1def30e --- /dev/null +++ b/libstark/src/languages/Acsp/AcspWitnessChecker.cpp @@ -0,0 +1,105 @@ +#include "AcspWitnessChecker.hpp" +#include "common/Utils/TaskReporting.hpp" +#include +#include + +using std::unique_ptr; +using std::vector; +using Algebra::FieldElement; + +namespace libstark { + + +/** + * @class notRootPredicate + * @brief A predicate class + * that checks if a given field element is + * not a root of some internally kept polynomial + */ +class notRootPredicate : public ::Algebra::FieldElementPredicate { +public: + /** + * @brief The constructor + * @param instnace that contains a constraint polynomial + * \f$P:\mathbb{F}^n \to \mathbb{F}\f$ and a neighbors polynomials + * vector \f$\vec{N}:\mathbb{F}^n \to \mathbb{F}^n\f$ + * @param witness that contains an assignment polynomial \f$A:\mathbb{F} \to \mathbb{F}\f$ + */ + notRootPredicate(const AcspInstance& instance, const AcspWitness& witness): instance_(instance), witness_(instance.prepareForWitnessChecker(witness)){}; + + /** + * @brief checks that the field element is not a root of some polynomial + * @param x the field element to check + * @return True iff \f$ 0 \neq P(x,A(N_1(x),A(N_2(x),\dots,A(N_n(x)) \f$ + */ + bool test(const ::Algebra::FieldElement& x)const { + vector assignment; + assignment.push_back(x); + + for(size_t wIndex = 0; wIndex < witness_.size(); wIndex++){ + for(const auto& n : instance_.neighborPolys()[wIndex]){ + assignment.push_back(witness_[wIndex]->eval(n->eval(x))); + } + } + return !instance_.constraintPoly().isRoot(assignment); + } + +private: + const AcspInstance& instance_; + const std::vector> witness_; +}; + + + +bool AcspWitnessChecker::verify(const AcspInstance& instance, const AcspWitness& witness){ + + TASK("Executes deterministic Acsp witness checker"); + + if (!verify_vanishing(instance,witness)) return false; + + if (!verify_witness_degree(instance,witness)) return false; + + if (!verify_boundary(instance,witness)) return false; + + return true; +} + +bool AcspWitnessChecker::verify_vanishing(const AcspInstance& instance, const AcspWitness& witness){ + TASK("Tests vanishing constraint"); + + const AcspInstance::set& vanisinigSet = instance.vanishingSet(); + notRootPredicate* predicate = new notRootPredicate(instance,witness); + + std::unique_ptr pred_ptr(predicate); + return !(vanisinigSet.exist(pred_ptr)); + +} + +bool AcspWitnessChecker::verify_witness_degree(const AcspInstance& instance, const AcspWitness& witness){ + TASK("Tests witness degree constraint"); + + bool isOK = true; + + for(size_t wIndex = 0; wIndex < witness.assignmentPolys().size(); wIndex++){ + isOK &= !(instance.witnessDegreeBound()[wIndex] < witness.assignmentPolys()[wIndex]->getDegree()); + } + + return isOK; +} + +bool AcspWitnessChecker::verify_boundary(const AcspInstance& instance, const AcspWitness& witness){ + TASK("Tests boundary constraints"); + + for(size_t wIndex = 0; wIndex < witness.assignmentPolys().size(); wIndex++){ + const AcspWitness::polynomial& assignment = *(witness.assignmentPolys()[wIndex]); + + for(const auto& p : instance.boundaryConstraints()[wIndex]){ + if(assignment.eval(p.first) != p.second) { + return false; + } + } + } + return true; +} + +} // namespace libstark diff --git a/libstark/src/languages/Acsp/AcspWitnessChecker.hpp b/libstark/src/languages/Acsp/AcspWitnessChecker.hpp new file mode 100644 index 0000000..309dd45 --- /dev/null +++ b/libstark/src/languages/Acsp/AcspWitnessChecker.hpp @@ -0,0 +1,65 @@ +#ifndef __AcspWitnessChecker_HPP +#define __AcspWitnessChecker_HPP + +#include "AcspInstance.hpp" +#include "AcspWitness.hpp" + +namespace libstark { + +/** + * @class AcspWitnessChecker + * @brief class for Acsp (instance, witness) pairs checking + */ +class AcspWitnessChecker { +public: + /** + * @brief verifies all conditions + * @param instance a tuple \f$(\mathbb{F},H,\vec{N},P,WitnessDegreeBound,B)\f$ + * @param witness a polynomial \f$ A \in \mathbb{F}[x]\f$ + * @return return true if and only if + * \f{eqnarray*}{ + * \forall z \in H: (P \circ (x \vert A \circ \vec{N}))(z) = 0 \\ + * \deg A <= witnessDegreeBound \\ + * \lambda \cdot \deg(P \circ (x \vert A \circ \vec{N})) \le |\mathbb{F}| \\ + * \forall i \in [n] : x_i = A(I(i)) \\ + * \f} + */ + static bool verify(const AcspInstance& instance, const AcspWitness& witness); + + /** + * @brief verifies the vanishing subset condition + * @param instance a tuple \f$(\mathbb{F},H,\vec{N},P,WitnessDegreeBound,B)\f$ + * @param witness a polynomial \f$ A \in \mathbb{F}[x]\f$ + * @return return true if and only if + * \f$ + * \forall z \in H: (P \circ (x \vert A \circ \vec{N}))(z) = 0 + * \f$ + */ + static bool verify_vanishing(const AcspInstance& instance, const AcspWitness& witness); + + /** + * @brief verifies the witness degree condition + * @param instance a tuple \f$(\mathbb{F},H,\vec{N},P,WitnessDegreeBound,B)\f$ + * @param witness a polynomial \f$ A \in \mathbb{F}[x]\f$ + * @return return true if and only if + * \f$ + * \deg A <= witnessDegreeBound + * \f$ + */ + static bool verify_witness_degree(const AcspInstance& instance, const AcspWitness& witness); + + /** + * @brief verifies the boundary constraints condition + * @param instance a tuple \f$(\mathbb{F},H,\vec{N},P,WitnessDegreeBound,B)\f$ + * @param witness a polynomial \f$ A \in \mathbb{F}[x]\f$ + * @return return true if and only if + * \f$ + * \forall (x,y) \in B : A(x)=y$ + * \f$ + */ + static bool verify_boundary(const AcspInstance& instance, const AcspWitness& witness); +private: +}; + +} // namespace libstark +#endif // __AcspWitnessChecker_HPP diff --git a/libstark/src/languages/Acsp/RationalConstraintsSys.hpp b/libstark/src/languages/Acsp/RationalConstraintsSys.hpp new file mode 100644 index 0000000..6ecf8dd --- /dev/null +++ b/libstark/src/languages/Acsp/RationalConstraintsSys.hpp @@ -0,0 +1,48 @@ +#include + +#include +#include +#include + +#ifndef RAT_CONSTRAINTS_SYS_HPP__ +#define RAT_CONSTRAINTS_SYS_HPP__ + +namespace libstark{ + +/** + * @class RationalConstraintSys + * @brief An interface for representation of system of ratonal constraints. + * + * A rational constraint system \f$ \mathcal{C} \f$ is a pair \f$ \f$ + * + * set of ordered pairs multivariate polynomials over + * some variables set \f$ \mathcal{U} \f$ and a space \f$H\f$. + * each pair contains one polynomial called the enumerator and marked by \f$p\f$, + * and another called the denominator and marked by \f$q\f$. + * + * We say that an assignment \f$ \alpha:\mathcal{U} \to \mathbb{F} \f$ satisfies + * \f$ \mathcal{C} \f$ if and only if \f$ \forall (p,q) \in \mathcal{C} : q\left(\alpha\right)=0 \rightarrow p\left(\alpha\right)=0\f$. + * + * The evaluation of \f$ (p,q) \in \mathcal{C}\f$ is the evaluation of the rational function \f$ \frac{p}{q} \f$, + * in case \f$ q\left(\alpha\right) \f$ the output is undefined and an exception is thrown. + */ + +class RationalConstraintSys : public Algebra::MappingsSys{ +public: + virtual ~RationalConstraintSys(){}; + + /// the amount of inputs each polynomial expects + virtual size_t numVars() const = 0; + + size_t numMappings()const = 0; + virtual std::vector eval(const std::vector& assignment)const = 0; + RationalConstraintSys* clone()const = 0; + + /// verifies if the rational constraint system is satisfied by given assignment + bool verify(const std::vector& assignment)const = 0; + +}; // class ConstraintSys + +} // namespace libstark + +#endif // RAT_CONSTRAINTS_SYS_HPP__ diff --git a/libstark/src/languages/Bair/BairInstance.hpp b/libstark/src/languages/Bair/BairInstance.hpp new file mode 100644 index 0000000..5e965b0 --- /dev/null +++ b/libstark/src/languages/Bair/BairInstance.hpp @@ -0,0 +1,161 @@ +#include "ConstraintsSys.hpp" +#include "common/langCommon/Sequence.hpp" +#include "common/Infrastructure/Infrastructure.hpp" + +#include +#include + +#ifndef BairINSTANCE_HPP__ +#define BairINSTANCE_HPP__ + +namespace libstark{ + +/** + * @class BairInstance + * @brief class for Bair instance + * + * A Bair instance is a tuple \f$(\mathbb{F},d,\mathcal{V},\mathcal{C}_\mathcal{A}, \mathcal{C}_\pi, B, padding_\pi)\f$ such that: + * + * \f$\mathbb{F}\f$ is a field with characteristic 2. + * + * \f$ d \in \mathbb{N} \f$ is the domain size indicator, we define the domain \f$ \mathcal{D} = \{0 \dots 2^d-2\} \f$. + * + * \f$ \mathcal{V} \times \{0,1\} \f$ is the variables set over which the constraint systems are defined. + * + * \f$ \mathcal{C}_\mathcal{A} \f$ is the constraint system (ConstraintSys) for the assignment \f$ \mathcal{A} \f$. + * + * \f$ \mathcal{C}_\pi \f$ is the constraint system (ConstraintSys) for the permutation \f$ \pi \f$. + * + * \f$ B \in (\mathcal{D} \times \mathcal{V}) \times \mathbb{F} \f$ is the set of boundary constraints + * + * \f$ padding_\pi \in \mathbb{F}^\mathcal{V} \f$ is a hint, that is not verified, but will be explained later + * + * For two assignments \f$ \alpha , \beta : \mathcal{V} \to \mathbb{F} \f$ we define \f$ (\alpha,\beta):\mathcal{V} \times\{0,1\} \to \mathbb{F} \f$ by: + * \f[ + * [(\alpha,\beta)](v,i) = + * \begin{cases} + * \alpha\left(v\right) & i=0\\ + * \beta\left(v\right) & i=1 + * \end{cases} + * \f] + * + * A Bair instance \f$(\mathbb{F},d,\mathcal{V},\mathcal{C}_\mathcal{A}, \mathcal{C}_\pi, B)\f$ + * is satisfiable if and only if there exists: + * + * \f$ \mathcal{A} : \mathcal{D} \to (\mathcal{V} \to \mathbb{F}) \f$ which is called the assignment + * + * \f$ \pi : \mathcal{D} \to \mathcal {D} \f$ which is called the permutation + * + * such that the following constraints hold: + * + * Assignment constraints satisfaction: + * \f[ + * \forall n \in \mathcal{D} \setminus \{ 2^d - 2 \}, \forall p \in \mathcal{C}_\mathcal{A} : p(\mathcal{A}(n), \mathcal{A}(n+1)) = 0 + * \f] + * + * Permutation constraints satisfaction: + * \f[ + * \begin{align*} + * \left[\mathcal{V}\times\left\{ 1\right\} \right]\left(\mathcal{C}_\pi\right)\neq\emptyset\Rightarrow & \pi \in S_\mathcal{D}\text{ (meaning it is a permutation)}\\ + * \forall n \in \mathcal{D} \setminus \{2^d -2 \},\forall p \in \mathcal{C}_\pi : & p\left(\mathcal{A} \left(n\right),\mathcal{A} \left( \pi \left(n\right)\right)\right)=0 + * \end{align*} + * \f] + * + * The \f $padding_\pi \f$ hint is a vector such that \f$ \forall \alpha \in \mathbb{F}^\mathcal{V} \f$ it holds that: + * \f$ \exists p \in \mathcal{C}_\pi : & p\left(\alpha,padding_\pi \right) \ne 0 \f$ + * + * Boundary constraints: + * \f[ + * \forall ((x,y),\alpha) \in B : [\mathcal{A}(x)](y) = \alpha + * \f] + * + * In the code we give more descriptive names: + * we name \f$ \mathbb{F} \f$ as 'contextField' \n + * we name \f$ d \f$ as 'domainSizeIndicator' \n + * we name the cardinality \f$ | \mathcal{D} | \f$ as 'domainSize' \n + * we define \f$ \mathcal{V} = \{ 0 \dots \text{'vectorsLen'} \}\f$ \n + * we name \f$ \mathcal{C}_\mathcal{A} \f$ as 'constraintsAssignment' \n + * we name \f$ \mathcal{C}_\pi \f$ as 'constraintsPermutation' \n + * we name \f$ B \f$ as 'boundaryConstraints' \n + * + * + * Methods:\n + * Instance class contains only getters, + * constructor and a destructor. + */ + +class BairInstance { +public: + typedef std::unique_ptr constraintsPtr_t; + typedef std::pair point_t; + typedef std::map boundaryConstraints_t; + typedef Sequence permutation_t; + + BairInstance( + const size_t vectorsLen, + const short domainSizeIndicator, + constraintsPtr_t&& constraints_assignment, + constraintsPtr_t&& constraints_permutation, + const boundaryConstraints_t& boundaryConstraints, + const std::vector& paddingPi) + : + vectorsLen_(vectorsLen), + domainSizeIndicator_(domainSizeIndicator), + constraintsAssignment_(std::move(constraints_assignment)), + constraintsPermutation_(std::move(constraints_permutation)), + boundaryConstraints_(boundaryConstraints), + paddingPi_(paddingPi){}; + + BairInstance(BairInstance&& src) = default; + + BairInstance(const BairInstance& src) = delete; + + inline const size_t& vectorsLen()const { + return vectorsLen_; + } + + inline const short& domainSizeIndicator()const { + return domainSizeIndicator_; + } + + inline const size_t domainSize()const { + return Infrastructure::POW2(domainSizeIndicator_)-1; + } + + inline const ConstraintSys& constraintsAssignment()const { + return *constraintsAssignment_; + } + + inline const ConstraintSys& constraintsPermutation()const { + return *constraintsPermutation_; + } + + inline const boundaryConstraints_t& boundaryConstraints()const{ + return boundaryConstraints_; + } + + inline const std::vector paddingPi()const{ + return paddingPi_; + } + +private: + size_t vectorsLen_; + short domainSizeIndicator_; + + //Constraints system to verify only the coloring (also known as C_time) + constraintsPtr_t constraintsAssignment_; + + //Constraints system to verify both coloring and permutation (also known as C_mem) + constraintsPtr_t constraintsPermutation_; + + boundaryConstraints_t boundaryConstraints_; + + //hint + std::vector paddingPi_; + +}; //class BairInstance + +} // namespace libstark + + +#endif // BairINSTANCE_HPP__ diff --git a/libstark/src/languages/Bair/BairWitness.hpp b/libstark/src/languages/Bair/BairWitness.hpp new file mode 100644 index 0000000..4fd2bf9 --- /dev/null +++ b/libstark/src/languages/Bair/BairWitness.hpp @@ -0,0 +1,97 @@ +#ifndef Bair_WITNESS_HPP__ +#define Bair_WITNESS_HPP__ + +#include +#include "common/langCommon/Sequence.hpp" + +#include +#include + +namespace libstark{ + +/** + * @class BairWitness + * @brief class for Bair Witness + * + * A Bair witness for Bair instance \f$(\mathbb{F},d,\mathcal{V},\mathcal{C}_\mathcal{A}, \mathcal{C}_\pi, B)\f$ + * is a pair \f$ (\mathcal{A},\pi) \f$ such that: + * + * \f$ \mathcal{A} : \mathcal{D} \to (\mathcal{V} \to \mathbb{F}) \f$ which is called the assignment + * + * \f$ \pi : \mathcal{D} \to \mathcal {D} \f$ which is called the permutation + * + * For two assignments \f$ \alpha , \beta : \mathcal{V} \to \mathbb{F} \f$ we define \f$ (\alpha,\beta):\mathcal{V} \times\{0,1\} \to \mathbb{F} \f$ by: + * \f[ + * [(\alpha,\beta)](v,i) = + * \begin{cases} + * \alpha\left(v\right) & i=0\\ + * \beta\left(v\right) & i=1 + * \end{cases} + * \f] + * + * It said that a Bair instance \f$(\mathbb{F},d,\mathcal{V},\mathcal{C}_\mathcal{A}, \mathcal{C}_\pi, B)\f$ + * is satisfied by a Bair witness \f$ (\mathcal{A},\pi) \f$ the following constraints hold: + * + * Assignment constraints satisfaction: + * \f[ + * \forall n \in \mathcal{D} \setminus \{ 2^d - 2 \}, \forall p \in \mathcal{C}_\mathcal{A} : p(\mathcal{A}(n), \mathcal{A}(n+1)) = 0 + * \f] + * + * Permutation constraints satisfaction: + * \f[ + * \begin{align*} + * \left[\mathcal{V}\times\left\{ 1\right\} \right]\left(\mathcal{C}_\pi\right)\neq\emptyset\Rightarrow & \pi \in S_\mathcal{D}\text{ (meaning it is a permutation)}\\ + * \forall n \in \mathcal{D} \setminus \{2^d -2 \},\forall p \in \mathcal{C}_\pi : & p\left(\mathcal{A} \left(n\right),\mathcal{A} \left( \pi \left(n\right)\right)\right)=0 + * \end{align*} + * \f] + * + * Boundary constraints: + * \f[ + * \forall ((x,y),\alpha) \in B : [\mathcal{A}(x)](y) = \alpha + * \f] + * + * In the code we give more descriptive names: + * + * we name \f$ \mathcal{A} \f$ as 'get_assignment' \n + * we name \f$ \pi \f$ as 'permutation' \n + * + * + * Methods:\n + * Instance class contains only getters, + * constructor and a destructor. + */ +class BairWitness { +public: + typedef std::vector color_t; + typedef Sequence assignment_t; + typedef std::unique_ptr assignment_ptr; + + typedef Sequence permutation_t; + typedef std::unique_ptr permutation_ptr; + + BairWitness( + assignment_ptr&& assignment, + permutation_ptr&& permutation) + : + assignment_(std::move(assignment)), + permutation_(std::move(permutation)){}; + + BairWitness(BairWitness&& src) = default; + BairWitness(const BairWitness& src) = delete; + + inline color_t get_color(size_t vecIndex)const { + return assignment_->getElementByIndex(vecIndex); + } + + inline const permutation_t& permutation()const { + return *permutation_; + } + +private: + assignment_ptr assignment_; + permutation_ptr permutation_; +}; + +} // namespace libstark + +#endif // Bair_WITNESS_HPP__ diff --git a/libstark/src/languages/Bair/BairWitnessChecker.cpp b/libstark/src/languages/Bair/BairWitnessChecker.cpp new file mode 100644 index 0000000..3e989c0 --- /dev/null +++ b/libstark/src/languages/Bair/BairWitnessChecker.cpp @@ -0,0 +1,109 @@ +#include "BairWitnessChecker.hpp" +#include "common/Utils/TaskReporting.hpp" + +using std::vector; +using Algebra::FieldElement; + +namespace libstark { + +static bool isPermutation(size_t const numElements, const BairWitness::permutation_t& mapping){ + //We check the mapping is a bijection (permutation) + //by checking that it is injective + + //inImage[i] will be set to 'true' iff + //there exists j that is mapped to it + vector inImage(numElements); + + //initialization + for(size_t i=0; i< numElements; i++) inImage[i] = false; + + //Check if is an injection + for(size_t i=0; i< numElements; i++){ + size_t img = mapping.getElementByIndex(i); + + //check the image is inside {0 ... numElements-1} + if ((img < 0) || (img >= numElements)) return false; + + //check no element was mapped to img before (validate injectivity) + if (inImage[img]) return false; + + //mark in image + inImage[img] = true; + } + + return true; +} + +static bool isSatisfied(const ConstraintSys& sys, const BairWitness::color_t& c1, const BairWitness::color_t& c2){ + vector assignment(c1.begin(),c1.begin() + (sys.numVars()/2)); + assignment.insert(assignment.end(),c2.begin(),c2.begin() + (sys.numVars()/2)); + return sys.verify(assignment); +} + +bool BairWitnessChecker::verify(const BairInstance& instance, const BairWitness& witness){ + TASK("Executing Bair determenistic witness checker"); + if(!verify_permutation(instance,witness)) return false; + if(!verify_constraintsAssignment(instance,witness))return false; + if(!verify_constraintsPermutation(instance,witness))return false; + if(!verify_boundary(instance,witness))return false; + return true; +} + +bool BairWitnessChecker::verify_permutation(const BairInstance& instance, const BairWitness& witness){ + TASK("verifyin mapping is permutation"); + size_t numElements = instance.domainSize(); + return isPermutation(numElements,witness.permutation()); +} + +bool BairWitnessChecker::verify_constraintsAssignment(const BairInstance& instance, const BairWitness& witness){ + TASK("verifying assignment constraints"); + size_t domainSize = instance.domainSize(); + const ConstraintSys& constraintsAssignment = instance.constraintsAssignment(); + for (size_t i=0; i < domainSize-1; i++){ + BairWitness::color_t c1 = witness.get_color(i); + BairWitness::color_t c2 = witness.get_color(i+1); + bool currSatisfied = isSatisfied(constraintsAssignment,c1,c2); + if (!currSatisfied) { + return false; + } + } + + return true; +} + +bool BairWitnessChecker::verify_constraintsPermutation(const BairInstance& instance, const BairWitness& witness){ + TASK("verifying permutation constraints"); + size_t domainSize = instance.domainSize(); + const ConstraintSys& constraintsPermutation = instance.constraintsPermutation(); + const BairWitness::permutation_t& permutation = witness.permutation(); + for (size_t i=0; i < domainSize-1; i++){ + BairWitness::color_t c1 = witness.get_color(i); + size_t perm_next = permutation.getElementByIndex(i); + BairWitness::color_t c2 = witness.get_color(perm_next); + bool currSatisfied = isSatisfied(constraintsPermutation,c1,c2); + if (!currSatisfied) { + return false; + } + } + + return true; +} + +bool BairWitnessChecker::verify_boundary(const BairInstance& instance, const BairWitness& witness){ + TASK("verifying boundary constraints"); + + for(const auto& p : instance.boundaryConstraints()){ + const BairInstance::point_t inputLocation = p.first; + const size_t vector_index = inputLocation.first; + const size_t element_index = inputLocation.second; + + const BairWitness::color_t color = witness.get_color(vector_index); + assert(color.size() == instance.vectorsLen()); + if(!(p.second == color[element_index])) { + return false; + } + } + return true; +} + +} // namespace libstark diff --git a/libstark/src/languages/Bair/BairWitnessChecker.hpp b/libstark/src/languages/Bair/BairWitnessChecker.hpp new file mode 100644 index 0000000..415bfc6 --- /dev/null +++ b/libstark/src/languages/Bair/BairWitnessChecker.hpp @@ -0,0 +1,64 @@ +#ifndef Bair_WITNESS_CHECKER_HPP__ +#define Bair_WITNESS_CHECKER_HPP__ + +#include "BairInstance.hpp" +#include "BairWitness.hpp" + +namespace libstark { + +/** + * @class BairWitnessChecker + * @brief class for determenistic Bair Witness Checker + * + * For two assignments \f$ \alpha , \beta : \mathcal{V} \to \mathbb{F} \f$ we define \f$ (\alpha,\beta):\mathcal{V} \times\{0,1\} \to \mathbb{F} \f$ by: + * \f[ + * [(\alpha,\beta)](v,i) = + * \begin{cases} + * \alpha\left(v\right) & i=0\\ + * \beta\left(v\right) & i=1 + * \end{cases} + * \f] + * + * It said that a Bair instance \f$(\mathbb{F},d,\mathcal{V},\mathcal{C}_\mathcal{A}, \mathcal{C}_\pi, B)\f$ + * is satisfied be a Bair witness \f$ (\mathcal{A},\pi) \f$ the following constraints hold: + * + * Assignment constraints satisfaction: + * \f[ + * \forall n \in \mathcal{D} \setminus \{ 2^d - 2 \}, \forall p \in \mathcal{C}_\mathcal{A} : p(\mathcal{A}(n), \mathcal{A}(n+1)) = 0 + * \f] + * + * Permutation constraints satisfaction: + * \f[ + * \begin{align*} + * \left[\mathcal{V}\times\left\{ 1\right\} \right]\left(\mathcal{C}_\pi\right)\neq\emptyset\Rightarrow & \pi \in S_\mathcal{D}\text{ (meaning it is a permutation)}\\ + * \forall n \in \mathcal{D} \setminus \{2^d -2 \},\forall p \in \mathcal{C}_\pi : & p\left(\mathcal{A} \left(n\right),\mathcal{A} \left( \pi \left(n\right)\right)\right)=0 + * \end{align*} + * \f] + * + * Boundary constraints: + * \f[ + * \forall ((x,y),\alpha) \in B : [\mathcal{A}(x)](y) = \alpha + * \f] + */ +class BairWitnessChecker { +public: + /// verify the witness satisfies the instance + static bool verify(const BairInstance& instance, const BairWitness& witness); + + /// verify that \f$ \pi \f$ is a permutation, if required to be + static bool verify_permutation(const BairInstance& instance, const BairWitness& witness); + + /// verifies assignment constraints satisfaction + static bool verify_constraintsAssignment(const BairInstance& instance, const BairWitness& witness); + + /// verifies permutation constraints satisfaction + static bool verify_constraintsPermutation(const BairInstance& instance, const BairWitness& witness); + + /// verifies boundary constraints + static bool verify_boundary(const BairInstance& instance, const BairWitness& witness); +}; + + +} // namespace libstark + +#endif //Bair_WITNESS_CHECKER_HPP__ diff --git a/libstark/src/languages/Bair/ConstraintsSys.cpp b/libstark/src/languages/Bair/ConstraintsSys.cpp new file mode 100644 index 0000000..0d8ca62 --- /dev/null +++ b/libstark/src/languages/Bair/ConstraintsSys.cpp @@ -0,0 +1,104 @@ +#include "ConstraintsSys.hpp" + +namespace libstark{ +using std::vector; +using std::unique_ptr; +using Algebra::FieldElement; +using Algebra::PolynomialInterface; +using Algebra::PolynomialDegree; +using Algebra::zero; + +namespace{ +class linearCombPoly : public PolynomialInterface{ +public: + linearCombPoly(const ConstraintSys& cs, const vector& coeffs): cs_(cs.clone()), coeffs_(coeffs) {}; + + FieldElement eval(const vector& x)const{ + const vector resList = cs_->eval(x); + FieldElement res = zero(); + for(size_t i=0; i< resList.size(); i++){ + res += coeffs_[i] * resList[i]; + } + + return res; + } + + size_t numVars()const{ + return cs_->numVars(); + } + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + PolynomialDegree res = PolynomialDegree::getZeroPolyDegree(); + for( const auto& p : cs_->constraints()){ + res = std::max(res,p->getDegreeBound(inputDegrees)); + } + return res; + } + + unique_ptr clone()const{ + return unique_ptr(new linearCombPoly(*cs_,coeffs_)); + } + + bool isEffectiveInput(const size_t varId)const{ + return cs_->varUsed(varId); + } + +private: + const unique_ptr cs_; + vector coeffs_; +}; +} + +bool ConstraintSys::varUsed(const size_t varId) const{ + for(const auto& poly : constraints()){ + if(poly->isEffectiveInput(varId)){ + return true; + } + } + return false; +} + +size_t ConstraintSys::numMappings()const{ + return constraints().size(); +} + +vector ConstraintSys::eval(const vector& assignment)const{ + vector res(numMappings()); + for(unsigned int i=0; i< res.size() ;i++){ + res[i] = constraints()[i]->eval(assignment); + } + + return res; +} + +PolynomialInterface* ConstraintSys::getLinearComb(const vector& coeffs)const{ + linearCombPoly* res = new linearCombPoly(*this,coeffs); + return res; +} + +bool ConstraintSys::verify(const vector& assignment) const{ + const vector resList = eval(assignment); + for (const FieldElement& r: resList){ + if (r != Algebra::zero()){ + return false; + } + } + return true; +} + +Algebra::PolynomialDegree ConstraintSys::getMaximalDegree()const{ + using Algebra::PolynomialDegree; + using std::vector; + using std::max; + + const vector inputDegrees(numVars(),PolynomialDegree(1)); + PolynomialDegree res = PolynomialDegree::getZeroPolyDegree(); + + for(const auto& poly : constraints()){ + res = max(res,poly->getDegreeBound(inputDegrees)); + } + + return res; +} + +} // namespace libstark diff --git a/libstark/src/languages/Bair/ConstraintsSys.hpp b/libstark/src/languages/Bair/ConstraintsSys.hpp new file mode 100644 index 0000000..843af22 --- /dev/null +++ b/libstark/src/languages/Bair/ConstraintsSys.hpp @@ -0,0 +1,62 @@ +#include +#include + +#include +#include +#include + +#ifndef CONSTRAINTS_SYS_HPP__ +#define CONSTRAINTS_SYS_HPP__ + +namespace libstark{ + +/** + * @class ConstraintSys + * @brief An interface for representation of system of constraints. + * + * A constraint system \f$ \mathcal{C} \f$ is a set of multivariate polynomials over + * some variables set \f$ \mathcal{U} \f$ . + * + * We say that an assignment \f$ \alpha:\mathcal{U} \to \mathbb{F} \f$ satisfies + * \f$ \mathcal{C} \f$ if and only if \f$ \forall p \in \mathcal{C} : p\left(\alpha\right)=0 \f$. + * + * For a given constraints system \f$ \mathcal{C} \f$ and a variables set \f$ \mathcal{V} \f$ + * we define \f$ \mathcal{V}\left(\mathcal{C}\right)\subset\mathcal{V} \f$ as the + * set of all variables \f$ v \f$ that are used in \f$ \mathcal{C} \f$ (it will be useful + * later for performance, when we notice some constraint systems use + * only a fraction of the variables set). + */ + +class ConstraintSys : public Algebra::MappingsSys{ +public: + typedef std::unique_ptr polyPtr_t; + typedef std::vector polySet_t; + + virtual ~ConstraintSys(){}; + + /// the amount of inputs each polynomial expects + virtual size_t numVars() const = 0; + + size_t numMappings()const; + virtual std::vector eval(const std::vector& assignment)const; + ConstraintSys* clone()const = 0; + + Algebra::PolynomialInterface* getLinearComb(const std::vector& coeffs)const; + + /// the constraint polynomials + virtual const polySet_t& constraints() const = 0; + + /// return false only if the variable with given ID does not affect any constraint polynomial + virtual bool varUsed(const size_t varId) const; + + /// verifies if the constraint system is satisfied by given assignment + bool verify(const std::vector& assignment)const; + + /// get maximal degree of a polynomial in the system + Algebra::PolynomialDegree getMaximalDegree()const; + +}; // class ConstraintSys + +} // namespace libstark + +#endif // CONSTRAINTS_SYS_HPP__ diff --git a/libstark/src/languages/Bair/ConstraintsSys.log b/libstark/src/languages/Bair/ConstraintsSys.log new file mode 100644 index 0000000..801aea1 --- /dev/null +++ b/libstark/src/languages/Bair/ConstraintsSys.log @@ -0,0 +1,2154 @@ +This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Arch Linux) (preloaded format=pdflatex 2017.11.21) 11 FEB 2018 17:04 +entering extended mode + \write18 enabled. + %&-line parsing enabled. +**PCP/src/languages/Bair/ConstraintsSys.hpp +(./PCP/src/languages/Bair/ConstraintsSys.hpp +LaTeX2e <2017-04-15> +Babel <3.15> and hyphenation patterns for 84 language(s) loaded. +! You can't use `macro parameter character #' in vertical mode. +l.1 # + include +? q +OK, entering \batchmode... + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.1 #i + nclude +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no < in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no L in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no P in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no I in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no . in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no > in font nullfont! +! You can't use `macro parameter character #' in horizontal mode. +l.2 # + include +Sorry, but I'm not programmed to handle this case; +I'll just pretend that you didn't ask for it. +If you're in the wrong mode, you might be able to +return to the right one by typing `I}' or `I$' or `I\par'. + +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no < in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no L in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no M in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no . in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no > in font nullfont! + +Overfull \hbox (20.0pt too wide) in paragraph at lines 1--3 +[] + [] + +! You can't use `macro parameter character #' in vertical mode. +l.4 # + include +Sorry, but I'm not programmed to handle this case; +I'll just pretend that you didn't ask for it. +If you're in the wrong mode, you might be able to +return to the right one by typing `I}' or `I$' or `I\par'. + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.4 #i + nclude +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no < in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no > in font nullfont! +! You can't use `macro parameter character #' in horizontal mode. +l.5 # + include +Sorry, but I'm not programmed to handle this case; +I'll just pretend that you didn't ask for it. +If you're in the wrong mode, you might be able to +return to the right one by typing `I}' or `I$' or `I\par'. + +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no < in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no > in font nullfont! +! You can't use `macro parameter character #' in horizontal mode. +l.6 # + include +Sorry, but I'm not programmed to handle this case; +I'll just pretend that you didn't ask for it. +If you're in the wrong mode, you might be able to +return to the right one by typing `I}' or `I$' or `I\par'. + +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no < in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no > in font nullfont! + +Overfull \hbox (20.0pt too wide) in paragraph at lines 4--7 +[] + [] + +! You can't use `macro parameter character #' in vertical mode. +l.8 # + ifndef CONSTRAINTS_SYS_HPP__ +Sorry, but I'm not programmed to handle this case; +I'll just pretend that you didn't ask for it. +If you're in the wrong mode, you might be able to +return to the right one by typing `I}' or `I$' or `I\par'. + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.8 #i + fndef CONSTRAINTS_SYS_HPP__ +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no O in font nullfont! +Missing character: There is no N in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no T in font nullfont! +Missing character: There is no R in font nullfont! +Missing character: There is no A in font nullfont! +Missing character: There is no I in font nullfont! +Missing character: There is no N in font nullfont! +Missing character: There is no T in font nullfont! +Missing character: There is no S in font nullfont! +! Missing $ inserted. + + $ +l.8 #ifndef CONSTRAINTS_ + SYS_HPP__ +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <7> on input line 8. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <5> on input line 8. +! Missing { inserted. + + _ +l.8 #ifndef CONSTRAINTS_SYS_HPP__ + +A left brace was mandatory here, so I've put one in. +You might want to delete and/or insert some corrections +so that I will find a matching right brace soon. +(If you're confused by all this, try typing `I}' now.) + +! Missing { inserted. + + ## +l.9 # + define CONSTRAINTS_SYS_HPP__ +A left brace was mandatory here, so I've put one in. +You might want to delete and/or insert some corrections +so that I will find a matching right brace soon. +(If you're confused by all this, try typing `I}' now.) + +! You can't use `macro parameter character #' in math mode. + ## + +l.9 # + define CONSTRAINTS_SYS_HPP__ +Sorry, but I'm not programmed to handle this case; +I'll just pretend that you didn't ask for it. +If you're in the wrong mode, you might be able to +return to the right one by typing `I}' or `I$' or `I\par'. + +! Missing { inserted. + + _ +l.9 #define CONSTRAINTS_SYS_HPP__ + +A left brace was mandatory here, so I've put one in. +You might want to delete and/or insert some corrections +so that I will find a matching right brace soon. +(If you're confused by all this, try typing `I}' now.) + +! Missing { inserted. + + \par +l.10 + +A left brace was mandatory here, so I've put one in. +You might want to delete and/or insert some corrections +so that I will find a matching right brace soon. +(If you're confused by all this, try typing `I}' now.) + +! Missing $ inserted. + + $ +l.10 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing } inserted. + + } +l.10 + +I've inserted something that you may have forgotten. +(See the above.) +With luck, this will get me unwedged. But if you +really didn't forget anything, try typing `2' now; then +my insertion and my current dilemma will both disappear. + +! Missing } inserted. + + } +l.10 + +I've inserted something that you may have forgotten. +(See the above.) +With luck, this will get me unwedged. But if you +really didn't forget anything, try typing `2' now; then +my insertion and my current dilemma will both disappear. + +! Missing } inserted. + + } +l.10 + +I've inserted something that you may have forgotten. +(See the above.) +With luck, this will get me unwedged. But if you +really didn't forget anything, try typing `2' now; then +my insertion and my current dilemma will both disappear. + +! Missing } inserted. + + } +l.10 + +I've inserted something that you may have forgotten. +(See the above.) +With luck, this will get me unwedged. But if you +really didn't forget anything, try typing `2' now; then +my insertion and my current dilemma will both disappear. + + +Overfull \hbox (20.0pt too wide) in paragraph at lines 8--10 +[] + [] + + +Overfull \hbox (151.49953pt too wide) in paragraph at lines 8--10 +[]\OML/cmm/m/it/10 YS[]PP[]$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.11 n + amespace libstark{ +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no n in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no P in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no P in font nullfont! +! Missing $ inserted. + + $ +l.11 namespace PCP_ + Project{ +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing $ inserted. + + $ +l.12 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing } inserted. + + } +l.12 + +I've inserted something that you may have forgotten. +(See the above.) +With luck, this will get me unwedged. But if you +really didn't forget anything, try typing `2' now; then +my insertion and my current dilemma will both disappear. + + +Overfull \hbox (20.0pt too wide) in paragraph at lines 11--12 +[] + [] + + +Overfull \hbox (33.58974pt too wide) in paragraph at lines 11--12 +[]\OML/cmm/m/it/10 roject[]$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.13 / + ** +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no / in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no @ in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no @ in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no A in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no . in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no A in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +! Undefined control sequence. +l.17 * A constraint system \f + $ \mathcal{C} \f$ is a set of multivariate pol... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.17 * A constraint system \f$ \mathcal{C} \f + $ is a set of multivariate pol... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no i in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +! Undefined control sequence. +l.18 * some variables set \f + $ \mathcal{U} \f$ . +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.18 * some variables set \f$ \mathcal{U} \f + $ . +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no . in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no W in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +! Undefined control sequence. +l.20 * We say that an assignment \f + $ \alpha:\mathcal{U} \to \mathbb{F} \f$ ... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.20 ...ignment \f$ \alpha:\mathcal{U} \to \mathbb + {F} \f$ satisfies +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.20 ...t \f$ \alpha:\mathcal{U} \to \mathbb{F} \f + $ satisfies +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no s in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no * in font nullfont! +! Undefined control sequence. +l.21 * \f + $ \mathcal{C} \f$ if and only if \f$ \forall p \in \mathcal{C} : p... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.21 * \f$ \mathcal{C} \f + $ if and only if \f$ \forall p \in \mathcal{C} : p... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +! Undefined control sequence. +l.21 * \f$ \mathcal{C} \f$ if and only if \f + $ \forall p \in \mathcal{C} : p... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.21 ...in \mathcal{C} : p\left(\alpha\right)=0 \f + $. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no . in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no F in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +! Undefined control sequence. +l.23 * For a given constraints system \f + $ \mathcal{C} \f$ and a variables s... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.23 ...iven constraints system \f$ \mathcal{C} \f + $ and a variables set \f$ ... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no a in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +! Undefined control sequence. +l.23 ...\f$ \mathcal{C} \f$ and a variables set \f + $ \mathcal{V} \f$ +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.23 ...\f$ and a variables set \f$ \mathcal{V} \f + $ +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no * in font nullfont! +Missing character: There is no w in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no e in font nullfont! +! Undefined control sequence. +l.24 * we define \f + $ \mathcal{V}\left(\mathcal{C}\right)\subset\mathcal{V} ... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.24 ...ft(\mathcal{C}\right)\subset\mathcal{V} \f + $ as the +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +! Undefined control sequence. +l.25 * set of all variables \f + $ v \f$ that are used in \f$ \mathcal{C} \f$ ... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.25 * set of all variables \f$ v \f + $ that are used in \f$ \mathcal{C} \f$ ... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +! Undefined control sequence. +l.25 ...ll variables \f$ v \f$ that are used in \f + $ \mathcal{C} \f$ (it will... +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.25 ... v \f$ that are used in \f$ \mathcal{C} \f + $ (it will be useful +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +Missing character: There is no ( in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no w in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no , in font nullfont! +Missing character: There is no w in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no w in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no ) in font nullfont! +Missing character: There is no . in font nullfont! +Missing character: There is no * in font nullfont! +Missing character: There is no / in font nullfont! + +Overfull \hbox (20.0pt too wide) in paragraph at lines 13--29 +[] + [] + + +Overfull \hbox (5.84865pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 C$ + [] + + +Overfull \hbox (7.25137pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 U$ + [] + + +Overfull \hbox (11.98953pt too wide) in paragraph at lines 13--29 +\OML/cmm/m/it/10 \OT1/cmr/m/n/10 : + [] + + +Overfull \hbox (20.0291pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 U ! + [] + + +Overfull \hbox (7.81946pt too wide) in paragraph at lines 13--29 +\OML/cmm/m/it/10 F$ + [] + + +Overfull \hbox (5.84865pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 C$ + [] + + +Overfull \hbox (20.03122pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 8\OML/cmm/m/it/10 p \OMS/cmsy/m/n/10 2 + [] + + +Overfull \hbox (11.40414pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 C \OT1/cmr/m/n/10 : + [] + + +Overfull \hbox (31.46523pt too wide) in paragraph at lines 13--29 +\OML/cmm/m/it/10 p [] \OT1/cmr/m/n/10 = + [] + + +Overfull \hbox (5.00002pt too wide) in paragraph at lines 13--29 +\OT1/cmr/m/n/10 0$ + [] + + +Overfull \hbox (5.84865pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 C$ + [] + + +Overfull \hbox (6.95004pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 V$ + [] + + +Overfull \hbox (32.79863pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 V [] ^^Z + [] + + +Overfull \hbox (6.95004pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 V$ + [] + + +Overfull \hbox (5.20601pt too wide) in paragraph at lines 13--29 +\OML/cmm/m/it/10 v$ + [] + + +Overfull \hbox (5.84865pt too wide) in paragraph at lines 13--29 +\OMS/cmsy/m/n/10 C$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.30 c + lass ConstraintSys : public Algebra::MappingsSys{ +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no A in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no M in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no q in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no e in font nullfont! +! Missing $ inserted. + + $ +l.32 typedef std::unique_ + ptr polyPtr_t; +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing $ inserted. + + $ +l.34 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + + +Overfull \hbox (20.0pt too wide) in paragraph at lines 30--34 +[] + [] + + +Overfull \hbox (23.57834pt too wide) in paragraph at lines 30--34 +[]\OML/cmm/m/it/10 tr < + [] + + +Overfull \hbox (43.16551pt too wide) in paragraph at lines 30--34 +\OML/cmm/m/it/10 Algebra \OT1/cmr/m/n/10 :: + [] + + +Overfull \hbox (107.67686pt too wide) in paragraph at lines 30--34 +\OML/cmm/m/it/10 PolynomialInterface > + [] + + +Overfull \hbox (98.44801pt too wide) in paragraph at lines 30--34 +\OML/cmm/m/it/10 polyPtr[]\OT1/cmr/m/n/10 ; \OML/cmm/m/it/10 typedefstd \OT1/cm +r/m/n/10 :: + [] + + +Overfull \hbox (37.99306pt too wide) in paragraph at lines 30--34 +\OML/cmm/m/it/10 vector < + [] + + +Overfull \hbox (48.32874pt too wide) in paragraph at lines 30--34 +\OML/cmm/m/it/10 polyPtr[] > + [] + + +Overfull \hbox (39.59497pt too wide) in paragraph at lines 30--34 +\OML/cmm/m/it/10 polySet[]\OT1/cmr/m/n/10 ;$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.35 v + irtual ~ConstraintSys(){}; +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no v in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no ( in font nullfont! +Missing character: There is no ) in font nullfont! +Missing character: There is no ; in font nullfont! + +Overfull \hbox (20.0pt too wide) in paragraph at lines 35--36 +[] + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.37 / + // the amount of inputs each polynomial expects +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no x in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no z in font nullfont! +Missing character: There is no e in font nullfont! +! Missing $ inserted. + + $ +l.38 virtual size_ + t numVars() const = 0; +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing $ inserted. + + $ +l.39 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + + +Overfull \hbox (20.0pt too wide) in paragraph at lines 37--39 +[] + [] + + +Overfull \hbox (88.65529pt too wide) in paragraph at lines 37--39 +[]\OML/cmm/m/it/10 numVars\OT1/cmr/m/n/10 ()\OML/cmm/m/it/10 const \OT1/cmr/m/n +/10 = + [] + + +Overfull \hbox (7.7778pt too wide) in paragraph at lines 37--39 +\OT1/cmr/m/n/10 0;$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.40 s + ize_t numMappings()const; +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no s in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no z in font nullfont! +Missing character: There is no e in font nullfont! +! Missing $ inserted. + + $ +l.40 size_ + t numMappings()const; +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Misplaced alignment tab character &. +l.41 ...(const std::vector& + assignment)const; +I can't figure out why you would want to use a tab mark +here. If you just want an ampersand, the remedy is +simple: Just type `I\&' now. But if some right brace +up above has ended a previous alignment prematurely, +you're probably due for more error messages, and you +might try typing `S' now just to see what is salvageable. + +! Missing $ inserted. + + $ +l.43 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + + +Overfull \hbox (146.96613pt too wide) in paragraph at lines 40--43 +[]$[]\OML/cmm/m/it/10 numMappings\OT1/cmr/m/n/10 ()\OML/cmm/m/it/10 const\OT1/c +mr/m/n/10 ; \OML/cmm/m/it/10 std \OT1/cmr/m/n/10 :: + [] + + +Overfull \hbox (37.99306pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 vector < + [] + + +Overfull \hbox (43.16551pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 Algebra \OT1/cmr/m/n/10 :: + [] + + +Overfull \hbox (73.70682pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 FieldElement > + [] + + +Overfull \hbox (67.53015pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 eval\OT1/cmr/m/n/10 (\OML/cmm/m/it/10 conststd \OT1/cmr/m/n/10 + :: + [] + + +Overfull \hbox (37.99306pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 vector < + [] + + +Overfull \hbox (43.16551pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 Algebra \OT1/cmr/m/n/10 :: + [] + + +Overfull \hbox (73.70682pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 FieldElement > + [] + + +Overfull \hbox (155.89772pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 assignment\OT1/cmr/m/n/10 )\OML/cmm/m/it/10 const\OT1/cmr/m/n/ +10 ; \OML/cmm/m/it/10 ConstraintSys \OMS/cmsy/m/n/10 ^^C + [] + + +Overfull \hbox (64.82304pt too wide) in paragraph at lines 40--43 +\OML/cmm/m/it/10 clone\OT1/cmr/m/n/10 ()\OML/cmm/m/it/10 const \OT1/cmr/m/n/10 += + [] + + +Overfull \hbox (7.7778pt too wide) in paragraph at lines 40--43 +\OT1/cmr/m/n/10 0;$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.44 / + // the constraint polynomials +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +! Missing $ inserted. + + $ +l.45 virtual const polySet_ + t& constraints() const = 0; +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Misplaced alignment tab character &. +l.45 virtual const polySet_t& + constraints() const = 0; +I can't figure out why you would want to use a tab mark +here. If you just want an ampersand, the remedy is +simple: Just type `I\&' now. But if some right brace +up above has ended a previous alignment prematurely, +you're probably due for more error messages, and you +might try typing `S' now just to see what is salvageable. + +! Missing $ inserted. + + $ +l.46 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + + +Overfull \hbox (20.0pt too wide) in paragraph at lines 44--46 +[] + [] + + +Overfull \hbox (96.62701pt too wide) in paragraph at lines 44--46 +[]\OML/cmm/m/it/10 constraints\OT1/cmr/m/n/10 ()\OML/cmm/m/it/10 const \OT1/cmr +/m/n/10 = + [] + + +Overfull \hbox (7.7778pt too wide) in paragraph at lines 44--46 +\OT1/cmr/m/n/10 0;$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.47 / + // return false only if the variable with given ID does not affect... + +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no w in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no I in font nullfont! +Missing character: There is no D in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no u in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no U in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no ( in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no z in font nullfont! +Missing character: There is no e in font nullfont! +! Missing $ inserted. + + $ +l.48 virtual bool varUsed(const size_ + t varId) const; +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing $ inserted. + + $ +l.49 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + + +Overfull \hbox (20.0pt too wide) in paragraph at lines 47--49 +[] + [] + + +Overfull \hbox (59.32996pt too wide) in paragraph at lines 47--49 +[]\OML/cmm/m/it/10 varId\OT1/cmr/m/n/10 )\OML/cmm/m/it/10 const\OT1/cmr/m/n/10 +;$ + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.50 / + // verifies if the constraint system is satisfied by given assignment + +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no ( in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no v in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no < in font nullfont! +Missing character: There is no A in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no F in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no E in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no > in font nullfont! +! Misplaced alignment tab character &. +l.51 ...(const std::vector& + assignment)const; +I can't figure out why you would want to use a tab mark +here. If you just want an ampersand, the remedy is +simple: Just type `I\&' now. But if some right brace +up above has ended a previous alignment prematurely, +you're probably due for more error messages, and you +might try typing `S' now just to see what is salvageable. + +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no ) in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no ; in font nullfont! + +Overfull \hbox (20.0pt too wide) in paragraph at lines 50--52 +[] + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.53 / + // get maximal degree of a polynomial in the system +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no x in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no h in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no A in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no b in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no : in font nullfont! +Missing character: There is no P in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no D in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no M in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no x in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no D in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no g in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no ( in font nullfont! +Missing character: There is no ) in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no ; in font nullfont! + +Overfull \hbox (20.0pt too wide) in paragraph at lines 53--55 +[] + [] + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.56 }; + // class ConstraintSys +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no ; in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no l in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no o in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no r in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no t in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no y in font nullfont! +Missing character: There is no s in font nullfont! + +Overfull \hbox (20.0pt too wide) in paragraph at lines 56--57 +[] + [] + +! Too many }'s. +l.58 } + // namespace libstark +You've closed more groups than you opened. +Such booboos are generally harmless, so keep going. + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.58 } / + / namespace libstark +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no m in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no s in font nullfont! +Missing character: There is no p in font nullfont! +Missing character: There is no a in font nullfont! +Missing character: There is no c in font nullfont! +Missing character: There is no e in font nullfont! +Missing character: There is no P in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no P in font nullfont! +! Missing $ inserted. + + $ +l.58 } // namespace PCP_ + Project +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing $ inserted. + + $ +l.59 + +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + + +Overfull \hbox (20.0pt too wide) in paragraph at lines 58--59 +[] + [] + + +Overfull \hbox (33.58974pt too wide) in paragraph at lines 58--59 +[]\OML/cmm/m/it/10 roject$ + [] + +! You can't use `macro parameter character #' in vertical mode. +l.60 # + endif // CONSTRAINTS_SYS_HPP__ +Sorry, but I'm not programmed to handle this case; +I'll just pretend that you didn't ask for it. +If you're in the wrong mode, you might be able to +return to the right one by typing `I}' or `I$' or `I\par'. + + +! LaTeX Error: Missing \begin{document}. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.60 #e + ndif // CONSTRAINTS_SYS_HPP__ +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +Missing character: There is no e in font nullfont! +Missing character: There is no n in font nullfont! +Missing character: There is no d in font nullfont! +Missing character: There is no i in font nullfont! +Missing character: There is no f in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no / in font nullfont! +Missing character: There is no C in font nullfont! +Missing character: There is no O in font nullfont! +Missing character: There is no N in font nullfont! +Missing character: There is no S in font nullfont! +Missing character: There is no T in font nullfont! +Missing character: There is no R in font nullfont! +Missing character: There is no A in font nullfont! +Missing character: There is no I in font nullfont! +Missing character: There is no N in font nullfont! +Missing character: There is no T in font nullfont! +Missing character: There is no S in font nullfont! +! Missing $ inserted. + + $ +l.60 #endif // CONSTRAINTS_ + SYS_HPP__ +I've inserted a begin-math/end-math symbol since I think +you left one out. Proceed, with fingers crossed. + +! Missing { inserted. + + _ +l.60 #endif // CONSTRAINTS_SYS_HPP__ + +A left brace was mandatory here, so I've put one in. +You might want to delete and/or insert some corrections +so that I will find a matching right brace soon. +(If you're confused by all this, try typing `I}' now.) + +) +! Emergency stop. +<*> PCP/src/languages/Bair/ConstraintsSys.hpp + +*** (job aborted, no legal \end found) + + +Here is how much of TeX's memory you used: + 9 strings out of 492985 + 290 string characters out of 6138637 + 56187 words of memory out of 5000000 + 3670 multiletter control sequences out of 15000+600000 + 3640 words of font info for 14 fonts, out of 8000000 for 9000 + 1141 hyphenation exceptions out of 8191 + 18i,5n,12p,147b,56s stack positions out of 5000i,500n,10000p,200000b,80000s +! ==> Fatal error occurred, no output PDF file produced! diff --git a/libstark/src/protocols/Ali/common_details/common.cpp b/libstark/src/protocols/Ali/common_details/common.cpp new file mode 100644 index 0000000..0aafa93 --- /dev/null +++ b/libstark/src/protocols/Ali/common_details/common.cpp @@ -0,0 +1,130 @@ +#include "common.hpp" + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace details{ + +phase_t advancePhase(const phase_t& currPhase){ + switch(currPhase){ + case START_PROTOCOL: return UNIVARIATE_COMMITMENTS; + case UNIVARIATE_COMMITMENTS : return VERIFIER_RANDOMNESS; + case VERIFIER_RANDOMNESS : return RS_PROXIMITY; + case RS_PROXIMITY : return QUERY; + case QUERY : return RESULTS; + default : return DONE; + } +} + +namespace PCP_common { + +using Algebra::FiniteSetInterface; +using Algebra::FieldElement; +using Algebra::PolynomialDegree; +using Algebra::PolynomialInterface; +using Algebra::mapIntegerToFieldElement; +using Infrastructure::Log2; +using std::max; +using std::ceil; +using std::vector; + +PolynomialDegree composition_div_ZH_degreeBound(const AcspInstance& src){ + // + // calculate degree bound of composition polynomial divided by the vanishing space poly + // + const FiniteSetInterface& vanishingSet = src.vanishingSet(); + const PolynomialInterface& constraintPoly = src.constraintPoly(); + + vector< PolynomialDegree> inputDegrees; + inputDegrees.push_back(PolynomialDegree(1)); + + for(size_t wIndex = 0; wIndex < src.neighborPolys().size(); wIndex++){ + const PolynomialDegree witnessDeg = src.witnessDegreeBound()[wIndex]; + for (const auto& n : src.neighborPolys()[wIndex]){ + inputDegrees.push_back(n->getDegreeBound(witnessDeg)); + } + } + + const auto compositionDegree = PolynomialDegree::integral_t(constraintPoly.getDegreeBound(inputDegrees)) - vanishingSet.size(); + + return PolynomialDegree(compositionDegree); +} + +vector witness_div_Z_Boundery_degreeBound(const AcspInstance& src){ + vector res; + for(size_t wIndex = 0; wIndex < src.neighborPolys().size(); wIndex++){ + res.push_back(PolynomialDegree(PolynomialDegree::integral_t(src.witnessDegreeBound()[wIndex]) - src.boundaryConstraints()[wIndex].size())); + } + + return res; +} + +/** + * Returns the basis over which the low degree tests are done + * (aka RS PCPP) + */ +basisWithShift_t basisForWitness(const AcspInstance& src){ + + // + // find the space size, it should be at least \f$ 2^{\Eta} \f$ times bigger + // than the maximal degree we will construct PCPP for + // + PolynomialDegree maxDegree = max(composition_div_ZH_degreeBound(src),PolynomialDegree(2)); + for (const auto& deg : witness_div_Z_Boundery_degreeBound(src)){ + maxDegree = max(maxDegree,deg); + } + const short maxDegreeLog = ceil(Log2(PolynomialDegree::integral_t(maxDegree))); + const unsigned short rankForPCPP_space = maxDegreeLog+SoundnessParameters::Eta+2; + + basisWithShift_t res; + + // + //Construct and return the standard basis of needed size + // + while(res.basis.size() < rankForPCPP_space){ + res.basis.push_back(mapIntegerToFieldElement(res.basis.size(),1,1)); + } + res.basis[res.basis.size()-1] -= mapIntegerToFieldElement(rankForPCPP_space ,1,1); + + // + //Construct the shift + // + res.shift = mapIntegerToFieldElement(rankForPCPP_space-1 ,1,1); + + return res; +} + +PolynomialDegree maximalPolyDegSupported_Witness(const AcspInstance& src){ + PolynomialDegree maxDegree(-1); + for (const auto& deg : witness_div_Z_Boundery_degreeBound(src)){ + maxDegree = max(maxDegree,deg); + } + + return maxDegree; +} + +/** + * Returns the basis over which the consistency of the witness and the composition + * polynomial is proved + */ +basisWithShift_t basisForConsistency(const AcspInstance& src){ + auto res = basisForWitness(src); + res.basis.resize(res.basis.size()-2); + return res; +} + +unsigned short boundaryPolysMatrix_logWidth(const AcspInstance& src){ + return ceil(Log2(src.neighborPolys().size()+1)); //+1 for ZK mask +} +unsigned short boundaryPolysMatrix_logNumElements(const AcspInstance& src){ + const unsigned short logNumWitnesses = boundaryPolysMatrix_logWidth(src); + const unsigned short witnessSpaceDim = basisForWitness(src).basis.size(); + return witnessSpaceDim + logNumWitnesses; +} + +} //namespace PCP_common + +} // namespace details +} // namespace Ali +} // namespace Protocols +} // namespace libstark diff --git a/libstark/src/protocols/Ali/common_details/common.hpp b/libstark/src/protocols/Ali/common_details/common.hpp new file mode 100644 index 0000000..cc4f933 --- /dev/null +++ b/libstark/src/protocols/Ali/common_details/common.hpp @@ -0,0 +1,109 @@ +#ifndef __PARTYSTATE_HPP__ +#define __PARTYSTATE_HPP__ + +#include "protocols/common/CryptoCommitment/MerkleCommitment.hpp" +#include "protocols/protocol.hpp" +#include "protocols/common/queries.hpp" +#include "languages/Acsp/AcspInstance.hpp" +#include +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace details{ + +template +class partyState{ +public: + virtual ~partyState(){}; + std::vector boundary; + T_univariate boundaryPolysMatrix; + T_univariate ZK_mask_boundary; + T_univariate ZK_mask_composition; +}; + +struct randomCoeefs{ + size_t degShift; + Algebra::FieldElement coeffUnshifted; + Algebra::FieldElement coeffShifted; + //multiplyes the polynomial by the polynomial: + // + *x^ + + randomCoeefs():degShift(0){}; +}; + +typedef partyState randomCoeffsSet_t; +typedef partyState rawQueries_t; +typedef partyState> rawResults_t; + +enum phase_t{START_PROTOCOL, UNIVARIATE_COMMITMENTS, VERIFIER_RANDOMNESS, RS_PROXIMITY, QUERY, RESULTS, DONE}; + +phase_t advancePhase(const phase_t& currPhase); + +class verifierMsg : public TranscriptMessage{ +public: + virtual ~verifierMsg(){}; + randomCoeffsSet_t randomCoefficients; + Ali::details::rawQueries_t queries; + std::unique_ptr RS_verifier_witness_msg; + std::unique_ptr RS_verifier_composition_msg; +}; + +class proverMsg : public TranscriptMessage{ +public: + virtual ~proverMsg(){}; + std::vector commitments; + Ali::details::rawResults_t results; + std::unique_ptr RS_prover_witness_msg; + std::unique_ptr RS_prover_composition_msg; +}; + +namespace PCP_common { + +Algebra::PolynomialDegree composition_div_ZH_degreeBound(const AcspInstance& src); + +std::vector witness_div_Z_Boundery_degreeBound(const AcspInstance& src); + +/** + * Returns the basis over which the low degree tests are done + * (aka RS PCPP) + */ +typedef std::vector basis_t; +struct basisWithShift_t{ + basis_t basis; + Algebra::FieldElement shift; + + basisWithShift_t(const basis_t& basis_, const Algebra::FieldElement& shift_): basis(basis_),shift(shift_){}; + basisWithShift_t(const basis_t& basis_): basis(basis_),shift(Algebra::zero()){}; + basisWithShift_t(): basis(0),shift(Algebra::zero()){}; +}; +basisWithShift_t basisForWitness(const AcspInstance& src); + +Algebra::PolynomialDegree maximalPolyDegSupported_Witness(const AcspInstance& src); + +/** + * Returns the basis over which the consistency of the witness and the composition + * polynomial is proved + */ +basisWithShift_t basisForConsistency(const AcspInstance& src); + +unsigned short boundaryPolysMatrix_logWidth(const AcspInstance& src); +unsigned short boundaryPolysMatrix_logNumElements(const AcspInstance& src); + +} // namespace PCP_common + +namespace SoundnessParameters{ + + const short Eta = 3; + const short SecurityParameter = 80; + +} // namespace SoundnessParameters + +} // namespace details +} // namespace Ali +} // namespace Protocols +} // namespace libstark + +#endif //#ifndef __PARTYSTATE_HPP__ diff --git a/libstark/src/protocols/Ali/prover.cpp b/libstark/src/protocols/Ali/prover.cpp new file mode 100644 index 0000000..7f3dba3 --- /dev/null +++ b/libstark/src/protocols/Ali/prover.cpp @@ -0,0 +1,894 @@ +#include "prover.hpp" + +#include "common/Utils/ErrorHandling.hpp" +#include "common/Utils/TaskReporting.hpp" +#include "protocols/Fri/prover.hpp" + +#include +#if __GNUG__ +#include +#endif + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace Prover{ + +using Infrastructure::Log2; +using Infrastructure::POW2; +using CryptoCommitment::hashDigest_t; +using CryptoCommitment::SparceMerkleTree; +using CryptoCommitment::getMerkleCommitmentInplace; +using Ali::details::randomCoeffsSet_t; +using Ali::details::rawQueries_t; +using Ali::details::rawResults_t; +using Ali::details::PCP_common::basisWithShift_t; +using Ali::details::PCP_common::composition_div_ZH_degreeBound; +using Ali::details::PCP_common::boundaryPolysMatrix_logWidth; +using Ali::details::PCP_common::boundaryPolysMatrix_logNumElements; +using Algebra::FieldElement; +using Algebra::PolynomialDegree; +using Algebra::UnivariatePolynomialInterface; +using Algebra::UnivariatePolynomialGeneral; +using Algebra::novelFFT; +using std::unique_ptr; +using std::set; +using std::vector; +// +// Proof constructions auxiliary functions +// +namespace{ + typedef vector coeffs_t; + + void getWitnessesCoefficients(const AcspInstance& acspInstance, const AcspWitness& acspWitness, coeffs_t* result){ + using Algebra::UnivariatePolynomialGeneral; + using Algebra::zero; + + const size_t numOfWitnesses = acspWitness.assignmentPolys().size(); + const size_t degBound = PolynomialDegree::integral_t(acspInstance.witnessDegreeBound()[0]); + for(const auto& d: acspInstance.witnessDegreeBound()){ + _COMMON_ASSERT(degBound == (size_t)PolynomialDegree::integral_t(d), "Degrees of witness columns are expected to be equal"); + } + + const unsigned short witnessDegLog = ceil(Log2(degBound)); + const size_t witnessPow2Deg = POW2(witnessDegLog); + +#pragma omp parallel for + for(unsigned int wIndex = 0; wIndex < numOfWitnesses; wIndex++){ + + const Algebra::UnivariatePolynomialInterface& witnessPoly = *(acspWitness.assignmentPolys()[wIndex]); + + if(acspInstance.boundaryConstraints()[wIndex].empty()){ + result[wIndex] = witnessPoly.getCoefficients(); + if (result[wIndex].size() < witnessPow2Deg){ + result[wIndex].resize(witnessPow2Deg,zero()); + } + + continue; + } + + unique_ptr polyToTest; + { + ///Compute the evaluation f:Sm->{0,1} such that forall i=1..|x| f(alpha_i)=x_i + ///where alpha_i is the i'th element of Sm and x_i is the i'th bit of the instance x. + ///And writing the input location to the vector S_x + Algebra::elementsSet_t S_x; + for (const auto& p : acspInstance.boundaryConstraints()[wIndex]) { + S_x.insert(p.first); + } + + UnivariatePolynomialGeneral Z_X(S_x); + + const auto deltaPoly = UnivariatePolynomialGeneral(acspInstance.boundaryConstraints()[wIndex]) - UnivariatePolynomialGeneral(witnessPoly); + polyToTest = Z_X.divideByMe(deltaPoly); + } + + //New block of witnesses + { + result[wIndex] = polyToTest->getCoefficients(); + if (result[wIndex].size() < witnessPow2Deg){ + result[wIndex].resize(witnessPow2Deg,zero()); + } + } + } + } + + void initWitness_ZkMask_coeffs(FieldElement* res, const size_t numCoeffs){ +#pragma omp parallel for + for(unsigned long long i =0; i< numCoeffs; i++){ + res[i] = Algebra::generateRandom(); + } + } + + unique_ptr boundaryPolysEvaluation(const AcspInstance& acspInstance, const AcspWitness& acspWitness, unique_ptr& fftInstance,bool& entireWitnessKept){ + using std::vector; + using Algebra::FieldElement; + + /**************************************/ + /** Step 1 - Parameter Instantiation: */ + /**************************************/ + + const size_t numOfWitnesses = acspWitness.assignmentPolys().size(); + const size_t numOfWitnessesWithZkMask = numOfWitnesses+1; + const size_t degBound = PolynomialDegree::integral_t(acspInstance.witnessDegreeBound()[0]); + for(const auto& d: acspInstance.witnessDegreeBound()){ + _COMMON_ASSERT(degBound == (size_t)PolynomialDegree::integral_t(d), "Degrees of witness columns are expected to be equal"); + } + const unsigned short witnessDegLog = ceil(Log2(degBound)); + const size_t witnessPow2Deg = POW2(witnessDegLog); + + // + //Construct ordered basis for proofs + // + const auto basisPCPP = Ali::details::PCP_common::basisForWitness(acspInstance); + + // + //Constructin evaluation + // + TASK("Constructing boundary constraints proof : evaluatin " + + std::to_string(numOfWitnesses) +" witnesses and 1 ZK MASK polynomial on spaces of dimension " + std::to_string(basisPCPP.basis.size())); + + vector witnessCoeffs(numOfWitnessesWithZkMask); + { + TASK("Computing witness coefficients"); + getWitnessesCoefficients(acspInstance,acspWitness,&witnessCoeffs[0]); + } + { + TASK("Generating Witnesses ZK Mask polynomial coefficients"); + const size_t zkIdx = witnessCoeffs.size()-1; + vector& zkMask = witnessCoeffs[zkIdx]; + + zkMask.resize(witnessPow2Deg); + initWitness_ZkMask_coeffs(&zkMask[0], witnessPow2Deg); + } + + { + TASK("Computing witness evaluations + transpose + Merkle"); + + // some space constants + const vector cosetBasis(basisPCPP.basis.begin(),basisPCPP.basis.begin()+witnessDegLog); + const vector shiftsBasis(basisPCPP.basis.begin()+witnessDegLog, basisPCPP.basis.end()); + const size_t numShifts = POW2(shiftsBasis.size()); + const size_t cosetSize = POW2(cosetBasis.size()); + + const unsigned short widthLog = boundaryPolysMatrix_logWidth(acspInstance); + const size_t width = POW2(widthLog); + + fftInstance = unique_ptr(new novelFFT(cosetBasis,std::move(witnessCoeffs),width,Algebra::zero())); + + unsigned short logNumCosetsInParallel; + { + unsigned short logRAM; + unsigned short logVM; + //compute RAM amount on current machine + { +#if __GNUG__ + struct sysinfo info; + sysinfo(&info); + logRAM = std::floor(Log2(info.totalram)); + logVM = std::floor(Log2(info.totalswap + info.totalram)); + logVM = std::min(logVM, logRAM); //it seems to be giving best performance +#else + logRAM = 33; + logVM = 33; +#endif + } + const unsigned short logCosetSize = widthLog + cosetBasis.size() + Log2(sizeof(FieldElement)); + entireWitnessKept = (logVM > shiftsBasis.size() + logCosetSize); + const int logMerkleOverhead = (entireWitnessKept? 1 : 0); + logNumCosetsInParallel = std::min(shiftsBasis.size(), (size_t)std::max(0,int(logRAM-(logCosetSize+logMerkleOverhead)))); + } + const size_t numCosetsInParallel = POW2(logNumCosetsInParallel); + + // + // Handeling the case where the proof fits entirely into RAM + // + if(entireWitnessKept == true){ + const bool proofFitsInRAM = (logNumCosetsInParallel >= shiftsBasis.size()); + + if(proofFitsInRAM){ + //Evaluate all cosets in parallel + TASK("Evaluating all witnesses, will keep them in RAM"); + return unique_ptr(new + dataWithCommitment(widthLog + cosetBasis.size(), shiftsBasis.size(), + [&](const size_t shiftIdx, FieldElement* res){ + const FieldElement affineShift = Algebra::getSpaceElementByIndex(shiftsBasis, basisPCPP.shift, shiftIdx); + fftInstance->FFT({affineShift}, res, cosetSize*width); + } + )); + } + else{ + //Evaluate cosets in blocks + TASK("Evaluating witnesses in blocks of " + std::to_string(numCosetsInParallel) + " cosets in block, will keep them in RAM"); + return unique_ptr(new + dataWithCommitment(widthLog + cosetBasis.size() + logNumCosetsInParallel, shiftsBasis.size() - logNumCosetsInParallel, + [&](const size_t globalShiftIdx, FieldElement* res){ + + TASK("Constracting " + std::to_string(numCosetsInParallel) + " cosets of total " + std::to_string(numShifts)); + + vector affineShifts(numCosetsInParallel); + + const size_t shiftsStart = globalShiftIdx*numCosetsInParallel; + for(size_t shiftId=0; shiftId < numCosetsInParallel; shiftId++){ + affineShifts[shiftId] = Algebra::getSpaceElementByIndex(shiftsBasis, basisPCPP.shift, shiftsStart + shiftId); + + } + fftInstance->FFT(affineShifts, res, cosetSize*width); + }, + false + )); + } + } + + vector merklePeakData(numShifts*sizeof(hashDigest_t)/sizeof(FieldElement)); + hashDigest_t* cosetsHash = (hashDigest_t*)&merklePeakData[0]; + { + vector cosetEval(width*cosetSize*numCosetsInParallel); + for(size_t i=0; i< numShifts/numCosetsInParallel; i++){ + TASK("Evaluating " + std::to_string(numCosetsInParallel) + " coset of " + std::to_string(numShifts)); + + //init offsets for cosets + vector shifts(numCosetsInParallel); + for(size_t shiftIdx=0; shiftIdx < numCosetsInParallel; shiftIdx++){ + shifts[shiftIdx] = Algebra::getSpaceElementByIndex(shiftsBasis, basisPCPP.shift, i*numCosetsInParallel + shiftIdx); + } + + //evaluate and collect hashes + + const unsigned int max_threads_machine = omp_get_max_threads(); + if(numCosetsInParallel < max_threads_machine){ + + //evaluate + fftInstance->FFT(shifts, &cosetEval[0], cosetSize*width); + + for(size_t shiftIdx=0; shiftIdx < numCosetsInParallel; shiftIdx++){ + FieldElement* currCoset = &cosetEval[shiftIdx*cosetSize*width]; + + //get coset Merkle root + cosetsHash[numCosetsInParallel*i + shiftIdx] = getMerkleCommitmentInplace(currCoset,Log2(sizeof(FieldElement)) + widthLog + cosetBasis.size()); + } + + } + else{ +#pragma omp parallel for + for(unsigned long long shiftIdx=0; shiftIdx < numCosetsInParallel; shiftIdx++){ + FieldElement* currCoset = &cosetEval[shiftIdx*cosetSize*width]; + + //evaluate + fftInstance->FFT({shifts[shiftIdx]}, currCoset, cosetSize*width); + + //get coset Merkle root + cosetsHash[numCosetsInParallel*i + shiftIdx] = getMerkleCommitmentInplace(currCoset,Log2(sizeof(FieldElement)) + widthLog + cosetBasis.size()); + } + } + } + } + + return unique_ptr(new dataWithCommitment(std::move(merklePeakData),0)); + } + } + + namespace{ + class cachedPoly_t : public Algebra::UnivariatePolynomialInterface{ + public: + cachedPoly_t( + const dataWithCommitment& model, + const size_t numColumns, + const size_t columnIdx, + const UnivariatePolynomialInterface& realModel, + const basisWithShift_t& basisPCPP, + const UnivariatePolynomialGeneral& vanishingPoly, + const UnivariatePolynomialGeneral& boundValsPoly, + const bool hasBoundary + ): + model_(model), + realModel_(realModel), + numColumns_(numColumns), + columnIdx_(columnIdx), + basisPCPP_(basisPCPP), + vanishingPoly_(vanishingPoly), + boundValsPoly_(boundValsPoly), + hasBoundary_(hasBoundary){ + + //verify basisPCPP is of the expected form + { + for(unsigned int i=0; i+1 clone()const{ + _COMMON_FATAL("NOT implemented"); + } + + vector eval(const vector& orderedBasis, const FieldElement& shift)const{ + _COMMON_FATAL("NOT implemented"); + } + + + private: + const dataWithCommitment& model_; + const UnivariatePolynomialInterface& realModel_; + const size_t numColumns_; + const size_t columnIdx_; + const basisWithShift_t basisPCPP_; + const UnivariatePolynomialGeneral vanishingPoly_; + const UnivariatePolynomialGeneral boundValsPoly_; + const bool hasBoundary_; + }; + + AcspWitness getCachedWitness(const AcspInstance& instance, const AcspWitness& witness, const dataWithCommitment& boundaryPolysMatrix){ + const auto PCPP_space = Ali::details::PCP_common::basisForWitness(instance); + + vector> res(witness.assignmentPolys().size()); + for(size_t i=0; i< witness.assignmentPolys().size(); i++){ + + //get the neighbors values for the consistency test + UnivariatePolynomialGeneral vanishingPoly(Algebra::one()); + UnivariatePolynomialGeneral boundValsPoly(Algebra::zero()); + const auto& currBoundaryConstraints = instance.boundaryConstraints()[i]; + + bool hasBoundary = false; + if(!currBoundaryConstraints.empty()){ + hasBoundary = true; + Algebra::elementsSet_t S_x; + for (const auto& p : currBoundaryConstraints) { + S_x.insert(p.first); + } + + vanishingPoly = UnivariatePolynomialGeneral(S_x); + boundValsPoly = UnivariatePolynomialGeneral(currBoundaryConstraints); + } + + res[i] = unique_ptr( + new cachedPoly_t(boundaryPolysMatrix,POW2(boundaryPolysMatrix_logWidth(instance)),i,*(witness.assignmentPolys()[i]),PCPP_space,vanishingPoly,boundValsPoly,hasBoundary)); + } + + return AcspWitness(std::move(res)); + } + } + + vector compositionPolysEvaluation(const AcspInstance& acspInstance, const AcspWitness& acspWitness, const vector& basis, const FieldElement& shift, const dataWithCommitment& boundaryPolysMatrix, const bool entireWitnessKept){ + + using Algebra::zero; + using std::unique_ptr; + + /**************************************/ + /** Step 1 - Parameter Instantiation: */ + /**************************************/ + + /** Filling in the parameters values according to the given parameter t */ + + // + //Construct ordered basises for proofs + // + const auto basisPCPP = Ali::details::PCP_common::basisForConsistency(acspInstance); + + /***************************************************************/ + /** Step 3 - Proof that A satisfies the constraint polynomial: */ + /***************************************************************/ + TASK("Computing B and p1. Size of p0 = " + std::to_string(POW2(basisPCPP.basis.size()))); + + vector polyEval; + if(entireWitnessKept){ + const auto cachedWitness = getCachedWitness(acspInstance,acspWitness,boundaryPolysMatrix); + polyEval = acspInstance.composeWithWitness_and_divideByVanishingSpacePoly(cachedWitness,basis,shift,true); + } + else{ + polyEval = acspInstance.composeWithWitness_and_divideByVanishingSpacePoly(acspWitness,basis,shift); + } + + return polyEval; + } + + unique_ptr ZK_Composition_PolyEvaluation(const AcspInstance& acspInstance){ + TASK("Generating mask poly for Composition RS proximity ZK"); + const size_t degBound = PolynomialDegree::integral_t(Ali::details::PCP_common::composition_div_ZH_degreeBound(acspInstance)); + UnivariatePolynomialGeneral ZK_poly; + for(size_t i=0; i <= degBound; i++){ + ZK_poly.setCoefficient(i, Algebra::generateRandom()); + } + + const auto basisPCPP = Ali::details::PCP_common::basisForConsistency(acspInstance); + return unique_ptr(new UniEvalWithCommitment(ZK_poly,basisPCPP.basis,basisPCPP.shift,ceil(Log2(degBound+1)))); + } + + vector computeUnivariateForPCPP_Witness(const AcspInstance& acspInstance, + const novelFFT& fftInstance, + const randomCoeffsSet_t& randCoeffs){ + + TASK("Constructing univariate polynomial for Witness PCPP proof"); + + // + //Construct ordered basises for proofs + // + const auto basisPCPP = Ali::details::PCP_common::basisForWitness(acspInstance); + const size_t numColumns = POW2(ceil(Log2(1 + acspInstance.witnessDegreeBound().size()))); //+1 for ZK mask poly + + const size_t degBound = PolynomialDegree::integral_t(acspInstance.witnessDegreeBound()[0]); + const unsigned short witnessDegLog = ceil(Log2(degBound)); + const vector cosetBasis(basisPCPP.basis.begin(),basisPCPP.basis.begin()+witnessDegLog); + const size_t cosetSize = POW2(cosetBasis.size()); + + vector evaluation(cosetSize*numColumns); + fftInstance.FFT({Algebra::zero()},&evaluation[0],0); + + vector cosetVals(cosetSize); + const size_t zkMaskIdx = acspInstance.boundaryConstraints().size(); + + const unsigned short logBlockLen = std::min(10,int(cosetBasis.size())); + const size_t blockLen = POW2(logBlockLen); + const size_t numBlocks = POW2(cosetBasis.size() - logBlockLen); +#pragma omp parallel for + for (unsigned long long blockIdx =0; blockIdx < numBlocks; blockIdx++){ + for (unsigned long long inBlockIdx = 0; inBlockIdx < blockLen; inBlockIdx++){ + + const size_t xIdx = blockIdx*blockLen + inBlockIdx; + const FieldElement x = getSpaceElementByIndex(cosetBasis,Algebra::zero(),xIdx); + + //the ZK mask poly + cosetVals[xIdx] = evaluation[xIdx*numColumns + zkMaskIdx]; + + //witnesses + for(size_t wIndex =0; wIndex cosetShiftsBasis(basisPCPP.basis.begin()+witnessDegLog, basisPCPP.basis.end()); + vector cosetShifts(POW2(cosetShiftsBasis.size())); + for(size_t i=0; i< cosetShifts.size(); i++){ + cosetShifts[i] = getSpaceElementByIndex(cosetShiftsBasis,basisPCPP.shift, i); + } + + vector res(POW2(basisPCPP.basis.size())); + fftCol.FFT(cosetShifts,&res[0],cosetSize); + return res; + } + } + + vector computeUnivariateForPCPP_Composition(const AcspInstance& acspInstance, const AcspWitness& acspWitness, + const uniEvalsSet_t& uniEvals, + const randomCoeffsSet_t& randCoeffs, + const bool entireWitnessKept){ + + TASK("Constructing univariate polynomial for Composition PCPP proof"); + + // + //Construct ordered basises for proofs + // + const auto basisPCPP = Ali::details::PCP_common::basisForConsistency(acspInstance); + const size_t spaceSize = POW2(basisPCPP.basis.size()); + + vector evaluation = compositionPolysEvaluation(acspInstance,acspWitness, basisPCPP.basis, basisPCPP.shift, *uniEvals.boundaryPolysMatrix, entireWitnessKept); +#pragma omp parallel for + for (unsigned long long xIdx = 0; xIdx < spaceSize; xIdx++){ + + //the ZK mask poly + evaluation[xIdx] *= randCoeffs.ZK_mask_composition.coeffUnshifted; + evaluation[xIdx] += uniEvals.ZK_mask_composition->getElement(xIdx); + + } + + return evaluation; + } +} + +// +// Results retrieving auxiliary functions +// +namespace{ + vector fillBoundaryResults(const novelFFT& fftInstance, + const dataWithCommitment& merkleTop, + const AcspInstance& instance, + const rawQueries_t& rawQueries){ + + if(rawQueries.boundaryPolysMatrix.empty())return vector(0); + + + // + //Construct ordered basis for proofs + // + const auto basisPCPP = Ali::details::PCP_common::basisForWitness(instance); + + const size_t degBound = PolynomialDegree::integral_t(instance.witnessDegreeBound()[0]); + const unsigned short witnessDegLog = ceil(Log2(degBound)); + const vector cosetBasis(basisPCPP.basis.begin(),basisPCPP.basis.begin()+witnessDegLog); + const vector shiftsBasis(basisPCPP.basis.begin()+witnessDegLog, basisPCPP.basis.end()); + const size_t numShifts = POW2(shiftsBasis.size()); + const size_t cosetSize = POW2(cosetBasis.size()); + const unsigned short widthLog = boundaryPolysMatrix_logWidth(instance); + const unsigned short heightLog = basisPCPP.basis.size(); + const size_t width = POW2(widthLog); + + const unsigned short cosetShiftSize = cosetBasis.size()+widthLog; + const size_t cosetMask = (1UL<> queriesByCoset(numShifts); + vector cosetsWithQueries; + { + set cosetsWithQueriesSet; + for(const size_t& blockPairIndex : rawQueries.boundaryPolysMatrix){ + const size_t blockIndex = blockPairIndex<<1; + const size_t FElemIndex = CryptoCommitment::getElementIndex(blockIndex); + + const size_t FElemCosetIndex = FElemIndex>>cosetShiftSize; + const size_t FElemLocalIndex = FElemIndex & cosetMask; + const size_t blockLocalIndex = CryptoCommitment::getBlockIndex(FElemLocalIndex); + const size_t blockLocalPairIndex = blockLocalIndex>>1; + + queriesByCoset[FElemCosetIndex].push_back(blockLocalPairIndex); + cosetsWithQueriesSet.insert(FElemCosetIndex); + } + cosetsWithQueries.insert(cosetsWithQueries.end(),cosetsWithQueriesSet.begin(), cosetsWithQueriesSet.end()); + } + const size_t numCosetsToEvaluate = cosetsWithQueries.size(); + + // + //Calculate number of cosets in parallel for perfomance + // + size_t NumCosetsInParallel; + { + unsigned short logRAM; + //compute RAM amount on current machine + { +#if __GNUG__ + struct sysinfo info; + sysinfo(&info); + logRAM = Log2(std::round(info.totalram)); +#else + logRAM = 33; +#endif + } + logRAM = std::max(logRAM, (unsigned short)(widthLog + cosetBasis.size() + Log2(sizeof(FieldElement)) + 1)); + NumCosetsInParallel = std::min(numCosetsToEvaluate, (size_t)POW2(size_t(logRAM-(widthLog + cosetBasis.size() + Log2(sizeof(FieldElement)))))); + } + + // + // Results tree + // + CryptoCommitment::SparceMerkleTree resultsTree(widthLog + heightLog + Log2(sizeof(FieldElement))); + vector cosetsEval(NumCosetsInParallel*cosetSize*width); + + for(size_t blockIdx=0; blockIdx < numCosetsToEvaluate; blockIdx+= NumCosetsInParallel){ + const size_t currBlockSize = std::min(numCosetsToEvaluate-blockIdx,NumCosetsInParallel); + TASK("Evaluating " + std::to_string(currBlockSize) + " cosets of total " + std::to_string(numCosetsToEvaluate)); + + //construct affine shifts + vector shifts(currBlockSize); + for(size_t i=0; i,CryptoCommitment::path_t> pathPair_t; + const size_t numQueries = queries.size(); + vector paths(numQueries); + { + for(unsigned int q=0; q< numQueries; q++){ + const size_t blockLocalPairIndex = queries[q]; + const size_t blockLocalIndex = blockLocalPairIndex<<1; + const size_t FElemLocalIndex = CryptoCommitment::getElementIndex(blockLocalIndex); + + memcpy(&paths[q].first[0], &cosetEval[FElemLocalIndex], 2*sizeof(hashDigest_t)); + } + } + + //answer queries + const unsigned short treeLogLen = Log2(cosetSize*width*sizeof(FieldElement)); + const auto queryPaths = CryptoCommitment::getPathToBlocksInPlace(&cosetEval[0], treeLogLen, queries); + for(unsigned int q=0; q< numQueries; q++){ + const size_t blockLocalPairIndex = queries[q]; + const size_t blockLocalIndex = blockLocalPairIndex<<1; + const size_t FElemLocalIndex = CryptoCommitment::getElementIndex(blockLocalIndex); + + const size_t FElemIndex = FElemLocalIndex | (shiftIdx<>1; + + paths[q].second = queryPaths[q]; + + paths[q].second.insert(paths[q].second.end(),merkleTopPath.begin(),merkleTopPath.end()); + + { + resultsTree.addPath(paths[q].first, paths[q].second, blockPairIndex); + } + } + } + } + else{ +#pragma omp parallel for + for(unsigned long long i=0; i,CryptoCommitment::path_t> pathPair_t; + const size_t numQueries = queries.size(); + vector paths(numQueries); + { + for(unsigned int q=0; q< numQueries; q++){ + const size_t blockLocalPairIndex = queries[q]; + const size_t blockLocalIndex = blockLocalPairIndex<<1; + const size_t FElemLocalIndex = CryptoCommitment::getElementIndex(blockLocalIndex); + + memcpy(&paths[q].first[0], &cosetEval[FElemLocalIndex], 2*sizeof(hashDigest_t)); + } + } + + //answer queries + const unsigned short treeLogLen = Log2(cosetSize*width*sizeof(FieldElement)); + const auto queryPaths = CryptoCommitment::getPathToBlocksInPlace(&cosetEval[0], treeLogLen, queries); + for(unsigned int q=0; q< numQueries; q++){ + const size_t blockLocalPairIndex = queries[q]; + const size_t blockLocalIndex = blockLocalPairIndex<<1; + const size_t FElemLocalIndex = CryptoCommitment::getElementIndex(blockLocalIndex); + + const size_t FElemIndex = FElemLocalIndex | (shiftIdx<>1; + + paths[q].second = queryPaths[q]; + + paths[q].second.insert(paths[q].second.end(),merkleTopPath.begin(),merkleTopPath.end()); + +#pragma omp critical + { + resultsTree.addPath(paths[q].first, paths[q].second, blockPairIndex); + } + } + } + } + } + + return resultsTree.toVector(); + } + + rawResults_t fillResults(const uniEvalsSet_t& uniEvals,const novelFFT& fftInstance, + const AcspInstance& instance, + const rawQueries_t& rawQueries, + const bool entireWitnessKept){ + + rawResults_t results; + + { + { + TASK("ZK Composition Mask polynomial"); + results.ZK_mask_composition = uniEvals.ZK_mask_composition->answerQueries(rawQueries.ZK_mask_composition).toVector(); + } + + { + TASK("Boundary polynomials"); + if(entireWitnessKept){ + results.boundaryPolysMatrix = uniEvals.boundaryPolysMatrix->answerQueries(rawQueries.boundaryPolysMatrix).toVector(); + } + else{ + results.boundaryPolysMatrix = fillBoundaryResults(fftInstance, *uniEvals.boundaryPolysMatrix ,instance,rawQueries); + } + } + } + + //return paths + return results; + } +} + +prover_t::prover_t(const AcspInstance& instance, const AcspWitness& witness, const RS_proverFactory_t& RS_proverFactory): + instance_(instance), + witness_(witness), + RS_proverFactory_(RS_proverFactory), + phase_(Ali::details::phase_t::START_PROTOCOL){}; + +void prover_t::receiveMessage(const TranscriptMessage& msg){ + const Ali::details::verifierMsg& vMsg = dynamic_cast(msg); + + switch(phase_){ + + case(Ali::details::phase_t::START_PROTOCOL): + { + TASK("Recieved start protocol request)"); + evaluateBoundryPolys(); + evaluateZK_Composition_mask(); + phase_ = Ali::details::advancePhase(phase_); + } + break; + + case(Ali::details::phase_t::VERIFIER_RANDOMNESS): + { + TASK("Recieving randomness from verifier"); + + RS_prover_witness_ = RS_proverFactory_( + Ali::details::PCP_common::basisForWitness(instance_).basis, + computeUnivariateForRS_Proximity_Witness(vMsg.randomCoefficients), + true + ); + + RS_prover_composition_ = RS_proverFactory_( + Ali::details::PCP_common::basisForConsistency(instance_).basis, + computeUnivariateForRS_Proximity_Composition(vMsg.randomCoefficients), + false + ); + + phase_ = Ali::details::advancePhase(phase_); + } + + default: + if(vMsg.RS_verifier_witness_msg){ + TASK("Recieved message from Witness RS proximity verifier"); + RS_prover_witness_->receiveMessage(*vMsg.RS_verifier_witness_msg); + } + if(vMsg.RS_verifier_composition_msg){ + TASK("Recieved message from Composition RS proximity verifier"); + RS_prover_composition_->receiveMessage(*vMsg.RS_verifier_composition_msg); + } + { + //Assume queries sent only at the end + //the proof is destructed after answering to queries + if(!vMsg.queries.boundaryPolysMatrix.empty()){ + { + TASK("Deleting IOPP proofs"); + dynamic_cast(RS_prover_witness_.get())->deleteProof(); + dynamic_cast(RS_prover_composition_.get())->deleteProof(); + } + { + TASK("Answering queries"); + nextResults_ = answerQueries(vMsg.queries); + } + } + } + } + +} + +msg_ptr_t prover_t::sendMessage(){ + + msg_ptr_t pMsgPtr(new Ali::details::proverMsg()); + auto& pMsg = dynamic_cast(*pMsgPtr); + + switch(phase_){ + + case(Ali::details::phase_t::UNIVARIATE_COMMITMENTS): + { + TASK("Sending commitments"); + + pMsg.commitments.push_back(state_.ZK_mask_composition->getCommitment()); + pMsg.commitments.push_back(state_.boundaryPolysMatrix->getCommitment()); + + phase_ = Ali::details::advancePhase(phase_); + } + break; + + default: + TASK("Sending communication from RS proximity prover and queries results"); + + pMsg.RS_prover_witness_msg = RS_prover_witness_->sendMessage(); + pMsg.RS_prover_composition_msg = RS_prover_composition_->sendMessage(); + pMsg.results = nextResults_; + } + + return pMsgPtr; +} + +void prover_t::evaluateBoundryPolys(){ + state_.boundaryPolysMatrix = boundaryPolysEvaluation(instance_,witness_,fftInstance_,entireWitnessKept_); +} + +void prover_t::evaluateZK_Composition_mask(){ + state_.ZK_mask_composition = ZK_Composition_PolyEvaluation(instance_); +} + +vector prover_t::computeUnivariateForRS_Proximity_Witness(const Ali::details::randomCoeffsSet_t& randCoeffs)const{ + return computeUnivariateForPCPP_Witness(instance_, *fftInstance_, randCoeffs); +} + +vector prover_t::computeUnivariateForRS_Proximity_Composition(const Ali::details::randomCoeffsSet_t& randCoeffs)const{ + return computeUnivariateForPCPP_Composition(instance_, witness_, state_, randCoeffs,entireWitnessKept_); +} + +Ali::details::rawResults_t prover_t::answerQueries(const Ali::details::rawQueries_t& queries){ + return fillResults(state_, *fftInstance_, instance_, queries, entireWitnessKept_); +} + + +} // namespace Prover +} // namespace Ali +} // namespace Protocols +} // namespace libstark diff --git a/libstark/src/protocols/Ali/prover.hpp b/libstark/src/protocols/Ali/prover.hpp new file mode 100644 index 0000000..0fe6975 --- /dev/null +++ b/libstark/src/protocols/Ali/prover.hpp @@ -0,0 +1,69 @@ +#ifndef Ali_PROVER_HPP__ +#define Ali_PROVER_HPP__ + +#include "protocols/protocol.hpp" +#include "common_details/common.hpp" + +#include "languages/Acsp/AcspInstance.hpp" +#include "languages/Acsp/AcspWitness.hpp" + +#include "protocols/common/proofs.hpp" +#include "protocols/Fri/prover.hpp" +#include "common/Utils/TaskReporting.hpp" + +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace Prover{ + +typedef std::function(const std::vector& evaluationBasis, std::vector&& evaluation, const bool L0isMSB)> RS_proverFactory_t; + +const RS_proverFactory_t Biased_prover = [](const std::vector& evaluationBasis, std::vector&& evaluation, const bool L0isMSB){ + TASK("Generating prover for RS proximity the 'BIASED' version"); + + return std::unique_ptr(new Protocols::Fri::Prover::prover_t(evaluationBasis, std::move(evaluation), L0isMSB)); +}; + + +typedef Ali::details::partyState> uniEvalsSet_t; + +class prover_t : public PartieInterface{ +public: + prover_t(const AcspInstance& instance, const AcspWitness& witness, const RS_proverFactory_t& RS_proverFactory); + void receiveMessage(const TranscriptMessage& msg); + msg_ptr_t sendMessage(); + +private: + const AcspInstance& instance_; + const AcspWitness& witness_; + const RS_proverFactory_t RS_proverFactory_; + std::unique_ptr fftInstance_; + + uniEvalsSet_t state_; + bool entireWitnessKept_; + std::unique_ptr RS_prover_witness_; + std::unique_ptr RS_prover_composition_; + Ali::details::phase_t phase_; + Ali::details::rawResults_t nextResults_; + + // + // methods + // + void evaluateBoundryPolys(); + void evaluateZK_Witness_mask(); + void evaluateZK_Composition_mask(); + std::vector computeUnivariateForRS_Proximity_Witness(const Ali::details::randomCoeffsSet_t& randCoeffs)const; + std::vector computeUnivariateForRS_Proximity_Composition(const Ali::details::randomCoeffsSet_t& randCoeffs)const; + Ali::details::rawResults_t answerQueries(const Ali::details::rawQueries_t& queries); +}; + + +} // namespace Prover +} // namespace Ali +} // namespace Protocols +} // namespace libstark + +#endif //#ifndef Ali_PROVER_HPP__ diff --git a/libstark/src/protocols/Ali/verifier.cpp b/libstark/src/protocols/Ali/verifier.cpp new file mode 100644 index 0000000..1847678 --- /dev/null +++ b/libstark/src/protocols/Ali/verifier.cpp @@ -0,0 +1,533 @@ +#include "verifier.hpp" +#include "common_details/common.hpp" +#include "common/Utils/ErrorHandling.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include "common/Utils/TaskReporting.hpp" +#include + +#include + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace Verifier{ + +using Ali::details::randomCoeffsSet_t; +using Ali::details::rawQueries_t; +using Ali::details::rawResults_t; +using Infrastructure::Log2; +using Algebra::FieldElement; +using Algebra::PolynomialDegree; +using std::vector; + +verifier_t::verifier_t(const AcspInstance& instance, const RS_verifierFactory_t& RS_verifierFactory) : + instance_(instance), + phase_(Ali::details::phase_t::START_PROTOCOL) + { + + TASK("Constructing verifier"); + + { + verifyParams(instance); + } + + //Initialize + { + //Boundry - aka Witness + { + const short logSizeWitnesses = Ali::details::PCP_common::boundaryPolysMatrix_logNumElements(instance) + Log2(sizeof(FieldElement)); + + //boundary + state_.boundaryPolysMatrix = uniEvalView_t(logSizeWitnesses); + } + + //composition + { + const size_t basisPCPP_size = Ali::details::PCP_common::basisForConsistency(instance).basis.size(); + const short logSizeBytes = basisPCPP_size + Log2(sizeof(FieldElement)); + + //ZK_Composition_mask + state_.ZK_mask_composition = uniEvalView_t(logSizeBytes); + } + } + + //Generate random coefficients + generateRandomCoefficients(const_cast(randCoeffs_)); + + //Generate queries + generateQueries(RS_verifierFactory); +} + +void verifier_t::receiveMessage(const TranscriptMessage& msg){ + const Ali::details::proverMsg& pMsg = dynamic_cast(msg); + + switch(phase_){ + + case(Ali::details::phase_t::UNIVARIATE_COMMITMENTS): + { + TASK("Recieved commitments"); + + keepZK_Composition_maskCommitment(pMsg.commitments[0]); + keepWitnessCommitment(pMsg.commitments[1]); + + phase_ = Ali::details::advancePhase(phase_); + } + break; + + case(Ali::details::phase_t::RS_PROXIMITY): + { + TASK("Recieved message for RS proximity prover"); + + if(!RS_verifier_witness_->doneInteracting()){ + RS_verifier_witness_->receiveMessage(*pMsg.RS_prover_witness_msg); + } + + if(!RS_verifier_composition_->doneInteracting()){ + RS_verifier_composition_->receiveMessage(*pMsg.RS_prover_composition_msg); + } + + if(RS_verifier_witness_->doneInteracting() && RS_verifier_composition_->doneInteracting()){ + phase_ = Ali::details::advancePhase(phase_); + } + } + break; + + case(Ali::details::phase_t::RESULTS): + { + TASK("Recieved results"); + digestResults(pMsg.results); + fetchResults(); + phase_ = Ali::details::advancePhase(phase_); + } + break; + + default: + _COMMON_FATAL("Got into unexpected phase in the protocol"); + } +} + +msg_ptr_t verifier_t::sendMessage(){ + msg_ptr_t vMsgPtr(new Ali::details::verifierMsg()); + auto& vMsg = dynamic_cast(*vMsgPtr); + switch(phase_){ + + case(Ali::details::phase_t::START_PROTOCOL): + { + TASK("Sending start protocol request)"); + phase_ = Ali::details::advancePhase(phase_); + } + break; + + case(Ali::details::phase_t::VERIFIER_RANDOMNESS): + { + TASK("Sending random coefficients for unified RS proximity proof (including ZK rho)"); + vMsg.randomCoefficients = randCoeffs_; + phase_ = Ali::details::advancePhase(phase_); + } + + case(Ali::details::phase_t::RS_PROXIMITY): + { + TASK("Sending message from RS proximity verifier"); + vMsg.RS_verifier_witness_msg = RS_verifier_witness_->sendMessage(); + vMsg.RS_verifier_composition_msg = RS_verifier_composition_->sendMessage(); + } + break; + + case(Ali::details::phase_t::QUERY): + { + + TASK("Sending queries"); + vMsg.queries = getRawQueries(); + phase_ = Ali::details::advancePhase(phase_); + } + break; + + default: + _COMMON_FATAL("Got into unexpected phase in the protocol"); + } + + return vMsgPtr; +} + +void verifier_t::digestQueries(const queriesToInp_t& queriesToInput_witness, const queriesToInp_t& queriesToInput_composition){ + TASK("Verifier digests queries"); + + const auto basisPCPP(Ali::details::PCP_common::basisForWitness(instance_)); + const size_t numWitnesses = instance_.witnessDegreeBound().size(); + const size_t numWitnessColumns = Infrastructure::POW2(Ali::details::PCP_common::boundaryPolysMatrix_logWidth(instance_)); + + // + // Digest queries made to univariate of Witness PCPP + // + { + TASK("Digest queries to Witness (aka boundary) RS PCPP univariate"); + + //calculate total number of queries + + linearComb_witness_.resize(queriesToInput_witness.size()); + size_t currCombIdx = 0; + + for(const auto& q : queriesToInput_witness){ + + const size_t x = q.first; + const auto& resultsAddressList = q.second; + + auto& currComb = linearComb_witness_[currCombIdx++]; + { + currComb.initLocation(getSpaceElementByIndex(basisPCPP.basis,basisPCPP.shift,x)); + for(FieldElement* location : resultsAddressList){ + currComb.addAnswerPtr(location); + } + } + + { + { + const size_t zkMaskColumnIdx = numWitnesses; //last column, right after the witnesses + const size_t x_matrix = (x*numWitnessColumns) + zkMaskColumnIdx; + const size_t blockIndex_matrix = CryptoCommitment::getBlockIndex(x_matrix); + const short offsetInBlock_matrix = CryptoCommitment::getOffsetInBlock(x_matrix); + auto setValFunc = [&](const FieldElement& res){ currComb.ZK_mask_res = res; }; + + state_.boundaryPolysMatrix.queries[blockIndex_matrix][offsetInBlock_matrix].push_back(setValFunc); + } + + { + currComb.boundaryEval_res.resize(numWitnesses); + for(size_t wIdx = 0; wIdx < numWitnesses; wIdx++){ + const size_t x_matrix = (x*numWitnessColumns) + wIdx; + const size_t blockIndex_matrix = CryptoCommitment::getBlockIndex(x_matrix); + const short offsetInBlock_matrix = CryptoCommitment::getOffsetInBlock(x_matrix); + + auto setValFunc = [=,&currComb](const FieldElement& res){ currComb.boundaryEval_res[wIdx] = res; }; + state_.boundaryPolysMatrix.queries[blockIndex_matrix][offsetInBlock_matrix].push_back(setValFunc); + } + } + } + } + } + + // + // Digest queries made to univariate of Composition PCPP + // + { + TASK("Digest queries to Composition RS PCPP univariate"); + + const auto consistencySpace(Ali::details::PCP_common::basisForConsistency(instance_)); + + //calculate total number of queries + polyEvaluation_composition_.resize(queriesToInput_composition.size()); + size_t currCombIdx = 0; + + for(const auto& q : queriesToInput_composition){ + const size_t x = q.first; + const FieldElement alpha = getSpaceElementByIndex(consistencySpace.basis, consistencySpace.shift, x); + const size_t blockIndex = CryptoCommitment::getBlockIndex(x); + const short offsetInBlock = CryptoCommitment::getOffsetInBlock(x); + const auto& resultsAddressList = q.second; + + auto& currComb = polyEvaluation_composition_[currCombIdx++]; + { + currComb.init(instance_,alpha); + for(FieldElement* location : resultsAddressList){ + currComb.addAnswerPtr(location); + } + } + + { + //ZK mask + { + auto setValFunc = [&](const FieldElement& res){ currComb.ZK_mask_res = res; }; + state_.ZK_mask_composition.queries[blockIndex][offsetInBlock].push_back(setValFunc); + } + + //Witness polys + { + + for (size_t wIndex = 0; wIndex < numWitnesses; wIndex++){ + + const auto& neighbours = instance_.neighborPolys()[wIndex]; + size_t nsize = neighbours.size(); + + for (unsigned int affine_num = 0; affine_num < nsize; affine_num++){ + const FieldElement currNeighborVal = neighbours[affine_num]->eval(alpha); + + { + const size_t neighborValIndex = mapFieldElementToInteger(0,basisPCPP.basis.size(), currNeighborVal + basisPCPP.shift);//getSpaceIndexOfElement(basisPCPP.basis,basisPCPP.shift,currNeighborVal); + const size_t x_matrix = (neighborValIndex*numWitnessColumns) + wIndex; + const size_t blockIndex_matrix = CryptoCommitment::getBlockIndex(x_matrix); + const short offsetInBlock_matrix = CryptoCommitment::getOffsetInBlock(x_matrix); + + auto setValFunc = [=,&currComb](const FieldElement& res){ currComb.boundaryPoly_res[wIndex][affine_num] = res; }; + state_.boundaryPolysMatrix.queries[blockIndex_matrix][offsetInBlock_matrix].push_back(setValFunc); + } + } + } + } + } + } + } +} + +rawQueries_t verifier_t::getRawQueries()const{ + TASK("Verifier generates raw queries to be passed to prover"); + + rawQueries_t res; + + //ZK Composition mask + { + TASK("ZK Composition Mask polynomial"); + res.ZK_mask_composition = state_.ZK_mask_composition.getRawQuery(); + } + + //boundry + { + TASK("Boundry polynomials"); + res.boundaryPolysMatrix = state_.boundaryPolysMatrix.getRawQuery(); + } + + return res; +} + +void verifier_t::digestResults(const rawResults_t& rawResults){ + TASK("Verifier digests results"); + + //ZK Composition mask + { + TASK("ZK Composition Mask polynomial"); + state_.ZK_mask_composition.digestResults(rawResults.ZK_mask_composition); + } + + //boundry + { + TASK("Boundry polynomials"); + state_.boundaryPolysMatrix.digestResults(rawResults.boundaryPolysMatrix); + } +} + +bool verifier_t::verifyComitment()const{ + TASK("Verifying integrity of results with commitments"); + bool res = true; + + //ZK Composition mask + { + TASK("ZK Composition Mask polynomial"); + res &= state_.ZK_mask_composition.verifyComitment(); + } + + //boundry + { + TASK("Boundry polynomials"); + res &= state_.boundaryPolysMatrix.verifyComitment(); + } + + return res; +} + + +void verifier_t::fetchResults()const{ + TASK("Verifyer fetches results"); + + //ZK Composition mask + { + TASK("ZK Composition Mask polynomial"); + state_.ZK_mask_composition.fetchResults(); + } + + //boundry + { + TASK("Boundry polynomials"); + state_.boundaryPolysMatrix.fetchResults(); + } + + // + // Calculating linear combinations needed for Witness PCPP + // + { + TASK("Calculating " + std::to_string(linearComb_witness_.size()) + " Witness RS PCPP result from queries to univariate"); + for(const auto& currComb : linearComb_witness_){ + currComb.calculate_witness(randCoeffs_); + } + } + + // + // Calculating constraint poly evaluations and linear combinations needed for Composition PCPP + // + { + TASK("Calculating " + std::to_string(polyEvaluation_composition_.size()) + " Composition RS PCPP result from queries to univariate"); + for(const auto& currComb : polyEvaluation_composition_){ + currComb.calculate(randCoeffs_); + } + } +} + +void verifier_t::fillResultsAndCommitmentRandomly(){ + + //random results for local proofs + state_.ZK_mask_composition.fillResultsAndCommitmentRandomly(); + state_.boundaryPolysMatrix.fillResultsAndCommitmentRandomly(); + + RS_verifier_composition_->fillResultsAndCommitmentRandomly(); + RS_verifier_witness_->fillResultsAndCommitmentRandomly(); + + + fetchResults(); +} + +void verifier_t::verifyParams(const AcspInstance& instance){ + const short PCPP_spaceDim = Ali::details::PCP_common::basisForWitness(instance).basis.size(); + const short ContextField_Dim = Algebra::ExtensionDegree; + _COMMON_ASSERT((PCPP_spaceDim <= ContextField_Dim), + std::string("Can't construct a PCP proof for Acsp instance, because the context field is too small\n") + + std::string("The context field is of dimension ") + std::to_string(ContextField_Dim) + std::string("\n") + + std::string("The sub-space for PCPP proof must be at least of dimension ") + std::to_string(PCPP_spaceDim) + ); +} + +void verifier_t::keepZK_Composition_maskCommitment(const CryptoCommitment::hashDigest_t& commitment){ + state_.ZK_mask_composition.commitment = commitment; +} + +void verifier_t::keepWitnessCommitment(const CryptoCommitment::hashDigest_t& commitment){ + state_.boundaryPolysMatrix.commitment = commitment; +} + +void verifier_t::generateQueries(const RS_verifierFactory_t& RS_verifierFactory){ + TASK("VERIFIER QUERIES GENERATION"); + + { + TASK("RS proximity of witness (aka boundary)"); + const auto PCPP_Basis(Ali::details::PCP_common::basisForWitness(instance_)); + const auto deg_composition = Algebra::PolynomialDegree::integral_t(Ali::details::PCP_common::maximalPolyDegSupported_Witness(instance_)); + const short deg_log_composition = ceil(Infrastructure::Log2(deg_composition)); + { + specsPrinter witnessFriSpecs("FRI for witness (f) specifications"); + RS_verifier_witness_ = RS_verifierFactory(PCPP_Basis.basis, Ali::details::SoundnessParameters::SecurityParameter, deg_log_composition,true, witnessFriSpecs); + witnessFriSpecs.print(); + } + } + + { + TASK("RS proximity of composition"); + const auto PCPP_Basis(Ali::details::PCP_common::basisForConsistency(instance_)); + const auto deg_composition = Algebra::PolynomialDegree::integral_t(Ali::details::PCP_common::composition_div_ZH_degreeBound(instance_)); + const short deg_log_composition = ceil(Infrastructure::Log2(deg_composition)); + { + specsPrinter compFriSpecs("FRI for constraints (g) specifications"); + RS_verifier_composition_ = RS_verifierFactory(PCPP_Basis.basis, Ali::details::SoundnessParameters::SecurityParameter, deg_log_composition,false,compFriSpecs); + compFriSpecs.print(); + } + } + + digestQueries(RS_verifier_witness_->queriesToInput(),RS_verifier_composition_->queriesToInput()); +} + +void verifier_t::generateRandomCoefficients(Ali::details::randomCoeffsSet_t& randCoeffs){ + TASK("Verifier generates random coefficients"); + + //a coefficient for the ZK masks + randCoeffs.ZK_mask_boundary.coeffUnshifted = Algebra::generateRandom(); + randCoeffs.ZK_mask_composition.coeffUnshifted = Algebra::generateRandom(); + + { + //get degree bounds + const PolynomialDegree::integral_t requiredDegreeBound = PolynomialDegree::integral_t(Ali::details::PCP_common::maximalPolyDegSupported_Witness(instance_)); + + //boundary witnesses + const auto& degBounds = Ali::details::PCP_common::witness_div_Z_Boundery_degreeBound(instance_); + for(size_t wIndex =0 ; wIndex < instance_.witnessDegreeBound().size(); wIndex++){ + + Protocols::Ali::details::randomCoeefs currCoeffs; + + //calculate expected degree bound + const PolynomialDegree::integral_t bounderyPoly_bound = PolynomialDegree::integral_t(degBounds[wIndex]); + + //init degree shifts to fit to the bounds + currCoeffs.degShift = requiredDegreeBound - bounderyPoly_bound; + + //fill random coefficients + currCoeffs.coeffUnshifted = Algebra::generateRandom(); + currCoeffs.coeffShifted = Algebra::generateRandom(); + + //add random coefficient to list + randCoeffs.boundary.push_back(currCoeffs); + } + } +} + +bool verifier_t::verify()const{ + bool res = verifyComitment(); + + { + TASK("Calling Witness RS Proximity verifier"); + res &= RS_verifier_witness_->verify(); + } + + { + TASK("Calling Composition RS Proximity verifier"); + res &= RS_verifier_composition_->verify(); + } + + return res; +} + +bool verifier_t::doneInteracting()const{ + return phase_ == Ali::details::phase_t::DONE; +} + +size_t verifier_t::expectedCommitedProofBytes()const{ + const size_t basisPCPP_size = Ali::details::PCP_common::basisForWitness(instance_).basis.size(); + const size_t evaluationBytes = Infrastructure::POW2(basisPCPP_size) * sizeof(FieldElement); + const size_t evaluationWithCommitmentBytes = 2UL * evaluationBytes; + + // *2 for merkle + const size_t witnessMatrixSize = 2UL*Infrastructure::POW2(Ali::details::PCP_common::boundaryPolysMatrix_logNumElements(instance_)) * sizeof(FieldElement); + + //Witness matrix + witness ZK mask + const size_t localProofsBytes = evaluationWithCommitmentBytes + witnessMatrixSize; + const size_t RS_proximityProofBytes_witness = RS_verifier_witness_->expectedCommitedProofBytes(); + const size_t RS_proximityProofBytes_composition = RS_verifier_composition_->expectedCommitedProofBytes(); + + const size_t evaluationWithCommitmentBytes_composition = 2UL * evaluationBytes; + + const size_t localProofsBytes_composition = evaluationWithCommitmentBytes_composition * 2UL; //1 for composition and 1 for ZK mask + + return localProofsBytes + localProofsBytes_composition + RS_proximityProofBytes_witness + RS_proximityProofBytes_composition; +} + +size_t verifier_t::expectedSentProofBytes()const{ + + size_t numExpectedHashes = 0; + + { + //Adding 1 for the commitments + numExpectedHashes += 1UL + state_.ZK_mask_composition.expectedResultsLenght(); + numExpectedHashes += 1UL + state_.boundaryPolysMatrix.expectedResultsLenght(); + } + + const size_t localBytes = numExpectedHashes * sizeof(CryptoCommitment::hashDigest_t); + const size_t RS_proximityBytes_witness = RS_verifier_witness_->expectedSentProofBytes(); + const size_t RS_proximityBytes_composition = RS_verifier_composition_->expectedSentProofBytes(); + + return localBytes + RS_proximityBytes_witness + RS_proximityBytes_composition; +} + +size_t verifier_t::expectedQueriedDataBytes()const{ + + size_t numExpectedHashes = 0; + + { + numExpectedHashes += state_.ZK_mask_composition.expectedQueriedFieldElementsNum(); + numExpectedHashes += state_.boundaryPolysMatrix.expectedQueriedFieldElementsNum(); + } + + const size_t localBytes = numExpectedHashes * sizeof(FieldElement); + const size_t RS_proximityBytes_witness = RS_verifier_witness_->expectedQueriedDataBytes(); + const size_t RS_proximityBytes_composition = RS_verifier_composition_->expectedQueriedDataBytes(); + + return localBytes + RS_proximityBytes_witness + RS_proximityBytes_composition; +} + +} // namespace Verifier +} // namespace Ali +} // namespace Protocols +} // namespace libstark diff --git a/libstark/src/protocols/Ali/verifier.hpp b/libstark/src/protocols/Ali/verifier.hpp new file mode 100644 index 0000000..049970d --- /dev/null +++ b/libstark/src/protocols/Ali/verifier.hpp @@ -0,0 +1,79 @@ +#ifndef Ali_VERIFIER_HPP__ +#define Ali_VERIFIER_HPP__ + +#include "protocols/protocol.hpp" +#include "common_details/common.hpp" +#include "languages/Acsp/AcspInstance.hpp" + +#include "verifier_details/queriesGen.hpp" +#include "protocols/Fri/verifier.hpp" +#include "common/Utils/TaskReporting.hpp" +#include "common/Utils/specsPrint.hpp" + +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace Verifier{ + +typedef std::function(const std::vector evaluationBasis, const short securityLevel, const short logDegBound, const bool L0isMSB, specsPrinter& specsCollector)> RS_verifierFactory_t; + +const RS_verifierFactory_t Biased_verifier = [](const std::vector evaluationBasis, const short securityLevel, const short logDegBound, const bool L0isMSB, specsPrinter& specsCollector){ + TASK("Generating verifier for RS proximity the 'BIASED' version"); + + return std::unique_ptr(new Protocols::Fri::Verifier::verifier_t(evaluationBasis, securityLevel, logDegBound, L0isMSB, specsCollector)); +}; + +class verifier_t : public verifierInterface{ + public: + verifier_t(const AcspInstance& instance, const RS_verifierFactory_t& RS_verifierFactory); + void receiveMessage(const TranscriptMessage& msg); + msg_ptr_t sendMessage(); + bool doneInteracting()const; + bool verify()const; + size_t expectedCommitedProofBytes()const; + size_t expectedSentProofBytes()const; + size_t expectedQueriedDataBytes()const; + void fillResultsAndCommitmentRandomly(); + + + private: + const AcspInstance& instance_; + const Ali::details::randomCoeffsSet_t randCoeffs_; + + std::vector linearComb_witness_; + std::vector polyEvaluation_composition_; + Ali::details::partyState state_; + std::unique_ptr RS_verifier_witness_; + std::unique_ptr RS_verifier_composition_; + Ali::details::phase_t phase_; + + // + // methods + // + + //verifies the Acsp has sound parameters, asserts otherwise + static void verifyParams(const AcspInstance& instance); + void generateRandomCoefficients(Ali::details::randomCoeffsSet_t& randCoeffs); + void generateQueries(const RS_verifierFactory_t& RS_verifierFactory); + + void keepZK_Witness_maskCommitment(const CryptoCommitment::hashDigest_t& commitment); + void keepZK_Composition_maskCommitment(const CryptoCommitment::hashDigest_t& commitment); + void keepWitnessCommitment(const CryptoCommitment::hashDigest_t& commitment); + + void digestQueries(const queriesToInp_t& queriesToInput_witness, const queriesToInp_t& queriesToInput_composition); + Ali::details::rawQueries_t getRawQueries()const; + void digestResults(const Ali::details::rawResults_t& rawResults); + bool verifyComitment()const; + void fetchResults()const; +}; + + +} // namespace Verifier +} // namespace Ali +} // namespace Protocols +} // namespace libstark + +#endif //#ifndef Ali_VERIFIER_HPP__ diff --git a/libstark/src/protocols/Ali/verifier_details/queriesGen.cpp b/libstark/src/protocols/Ali/verifier_details/queriesGen.cpp new file mode 100644 index 0000000..297b7b8 --- /dev/null +++ b/libstark/src/protocols/Ali/verifier_details/queriesGen.cpp @@ -0,0 +1,124 @@ +#include "queriesGen.hpp" +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace Verifier{ +namespace details{ + +using std::pair; +using std::max; +using std::map; +using std::vector; +using Algebra::FieldElement; +using Algebra::zero; +using Infrastructure::POW2; + +void linearCombinationValue::initLocation(const FieldElement& x){ + x_ = x; +} + +void linearCombinationValue::addAnswerPtr(FieldElement* answerPtr){ + result_.addAnswerPtr(answerPtr); +} + +void linearCombinationValue::calculate_witness(const Protocols::Ali::details::randomCoeffsSet_t& coeffs)const{ + FieldElement res = ZK_mask_res; + + for(size_t i=0; i< coeffs.boundary.size(); i++){ + res += (coeffs.boundary[i].coeffUnshifted + (power(x_, coeffs.boundary[i].degShift) * coeffs.boundary[i].coeffShifted)) * boundaryEval_res[i]; + } + + result_.answer(res); +} + +void compositionWithZK_Value::addAnswerPtr(FieldElement* answerPtr){ + result_.addAnswerPtr(answerPtr); +} + +void compositionWithZK_Value::calculate(const Protocols::Ali::details::randomCoeffsSet_t& coeffs)const{ + FieldElement res = coeffs.ZK_mask_composition.coeffUnshifted * calculateCompositionValue() + ZK_mask_res; + + result_.answer(res); +} + +void compositionWithZK_Value::init(const AcspInstance& instance, const FieldElement& consistencyPoint){ + instance_ = &instance; + consistencyPoint_ = consistencyPoint; + + const size_t numWitnesses = instance.neighborPolys().size(); + + boundaryPoly_res.resize(numWitnesses); + boundaryVanishingVals_.resize(numWitnesses); + boundaryPolyVals_.resize(numWitnesses); + + for(size_t wIdx=0; wIdx < numWitnesses ; wIdx++){ + + auto& currNeighbors = instance_->neighborPolys()[wIdx]; + auto& currResults = boundaryPoly_res[wIdx]; + auto& currVanishingVals = boundaryVanishingVals_[wIdx]; + auto& currPolyVals = boundaryPolyVals_[wIdx]; + + currResults.resize(currNeighbors.size()); + currVanishingVals.resize(currNeighbors.size()); + currPolyVals.resize(currNeighbors.size()); + + const auto& currBoundaryConstraints = instance_->boundaryConstraints()[wIdx]; + + + //get the neighbors values for the consistency test + Algebra::UnivariatePolynomialGeneral Ax(currBoundaryConstraints); + + Algebra::elementsSet_t S_x; + for (const auto& p : currBoundaryConstraints) { + S_x.insert(p.first); + } + + Algebra::UnivariatePolynomialGeneral Z_X(S_x); + + for (unsigned int i=0; i < currNeighbors.size(); i++) { + const FieldElement currNeighborVal = currNeighbors[i]->eval(consistencyPoint_); + currVanishingVals[i] = Z_X.eval(currNeighborVal); + currPolyVals[i] = Ax.eval(currNeighborVal); + } + } + + + //get the vanishing space polynomial value for consistency test (aka Z_H) + { + vanishingSpacePolyVal_ = instance_->vanishingSet().vanishingPoly().eval(consistencyPoint_); + } +} + +FieldElement compositionWithZK_Value::calculateCompositionValue()const{ + vector inputValuesVector; + inputValuesVector.push_back(consistencyPoint_); + + const size_t numWitnesses = instance_->neighborPolys().size(); + + for(size_t wIdx=0; wIdx < numWitnesses ; wIdx++){ + + auto& currNeighbors = instance_->neighborPolys()[wIdx]; + auto& currResults = boundaryPoly_res[wIdx]; + auto& currVanishingVals = boundaryVanishingVals_[wIdx]; + auto& currPolyVals = boundaryPolyVals_[wIdx]; + + for (unsigned int i=0; i < currNeighbors.size(); i++) { + const FieldElement inputValue(currResults[i]); + inputValuesVector.push_back(inputValue*currVanishingVals[i] + currPolyVals[i]); + } + } + + const auto& acspPoly = instance_->constraintPoly(); + const FieldElement res = acspPoly.eval(inputValuesVector)/vanishingSpacePolyVal_; + + return res; +} + +} // namespace details +} // namespace Verifier +} // namespace Ali +} // namespace Protocols +} // namespace libstark diff --git a/libstark/src/protocols/Ali/verifier_details/queriesGen.hpp b/libstark/src/protocols/Ali/verifier_details/queriesGen.hpp new file mode 100644 index 0000000..0d7c7cf --- /dev/null +++ b/libstark/src/protocols/Ali/verifier_details/queriesGen.hpp @@ -0,0 +1,62 @@ +#ifndef ACP_IOP_VERIFIER_QUERIES_GEN_HPP__ +#define ACP_IOP_VERIFIER_QUERIES_GEN_HPP__ + +#include "../common_details/common.hpp" +#include "protocols/common/verifier.hpp" +#include "languages/Acsp/AcspInstance.hpp" +#include + +#include + +namespace libstark{ +namespace Protocols{ +namespace Ali{ +namespace Verifier{ +namespace details{ + +// +// Queries to input of inner RS proximity verifier +// + +class linearCombinationValue{ +public: + mutable std::vector boundaryEval_res; + mutable Algebra::FieldElement compositionEval_res; + mutable Algebra::FieldElement ZK_mask_res; + + void initLocation(const Algebra::FieldElement& x); + void calculate_witness(const Protocols::Ali::details::randomCoeffsSet_t& coeffs)const; + void addAnswerPtr(Algebra::FieldElement* answerPtr); +private: + Algebra::FieldElement x_; + ResultLocation result_; +}; + +class compositionWithZK_Value{ +public: + mutable std::vector> boundaryPoly_res; + mutable Algebra::FieldElement ZK_mask_res; + + void init(const AcspInstance& instance, const Algebra::FieldElement& consistencyPoint); + void calculate(const Protocols::Ali::details::randomCoeffsSet_t& coeffs)const; + void addAnswerPtr(Algebra::FieldElement* answerPtr); + +private: + const AcspInstance* instance_; + Algebra::FieldElement consistencyPoint_; + std::vector> boundaryVanishingVals_; + std::vector> boundaryPolyVals_; + Algebra::FieldElement vanishingSpacePolyVal_; + ResultLocation result_; + + //methods + Algebra::FieldElement calculateCompositionValue()const; +}; + +} // namespace details +} // namespace Verifier +} // namespace Ali +} // namespace Protocols +} // namespace libstark + +#endif // ACP_IOP_VERIFIER_QUERIES_GEN_HPP__ diff --git a/libstark/src/protocols/Fri/common/common.cpp b/libstark/src/protocols/Fri/common/common.cpp new file mode 100644 index 0000000..00d7541 --- /dev/null +++ b/libstark/src/protocols/Fri/common/common.cpp @@ -0,0 +1,62 @@ +#include "common.hpp" +#include + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace common{ + +using std::vector; +using Algebra::FieldElement; +using Algebra::zero; +using Algebra::elementsSet_t; +using Algebra::SubspacePolynomial; + +vector getL0Basis(const vector& BasisL, const bool L0isMSB){ + if(L0isMSB){ + return vector(BasisL.end() - SoundnessParameters::dimReduction, BasisL.end()); + } + + return vector(BasisL.begin(), BasisL.begin() + SoundnessParameters::dimReduction); +} + +vector getL1Basis(const vector& BasisL, const bool L0isMSB){ + if(L0isMSB){ + return vector(BasisL.begin(), BasisL.end() - SoundnessParameters::dimReduction); + } + return vector(BasisL.begin() + SoundnessParameters::dimReduction, BasisL.end()); +} + +size_t getBasisLIndex_byL0L1indices(const vector& BasisL, const size_t idxL0, const size_t idxL1, const bool L0isMSB){ + if(L0isMSB){ + const size_t BasisL1_size = BasisL.size() - SoundnessParameters::dimReduction; + return idxL1 | (idxL0< getColumnBasis(const vector& L, const bool L0isMSB){ + const vector L0Basis = getL0Basis(L, L0isMSB); + const elementsSet_t rowsBasis(L0Basis.begin(), L0Basis.end()); + + vector basisForColumn(getL1Basis(L, L0isMSB)); + { + const SubspacePolynomial q(rowsBasis); + const FieldElement q_on_ZERO = q.eval(zero()); + for(FieldElement& e : basisForColumn){ + e = q.eval(e + q_on_ZERO); + } + } + + return basisForColumn; +} + +unsigned short dimOfColumn(const unsigned short dimOfL){ + return dimOfL - SoundnessParameters::dimReduction; +} + +} //namespace common +} //nmasepace Fri +} //namespace Protocols +} //namespace libstark diff --git a/libstark/src/protocols/Fri/common/common.hpp b/libstark/src/protocols/Fri/common/common.hpp new file mode 100644 index 0000000..d68802f --- /dev/null +++ b/libstark/src/protocols/Fri/common/common.hpp @@ -0,0 +1,56 @@ +#ifndef RS_IOPP_BIASED_COMMON +#define RS_IOPP_BIASED_COMMON + +#include "protocols/protocol.hpp" +#include "protocols/common/queries.hpp" + +#include +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace common{ + +std::vector getL0Basis(const std::vector& L, const bool L0isMSB); +std::vector getL1Basis(const std::vector& L, const bool L0isMSB); +std::vector getColumnBasis(const std::vector& L, const bool L0isMSB); +size_t getBasisLIndex_byL0L1indices(const std::vector& BasisL, const size_t idxL0, const size_t idxL1, const bool L0isMSB); +unsigned short dimOfColumn(const unsigned short dimOfL); + +namespace SoundnessParameters{ + const unsigned short dimReduction = 2; +} //namespace SoundnessParameters + +template +class state_t{ +public: + T localState; + std::map , Algebra::classCompElements> subproofs; +}; + +typedef state_t rawQueries_t; + +typedef state_t rawResults_t; + +typedef std::vector subproofLocation_t; + +class verifierRequest_t : public TranscriptMessage{ + public: + std::vector proofConstructionQueries; + rawQueries_t dataQueries; +}; + +class proverResponce_t : public TranscriptMessage{ + public: + std::vector proofConstructionComitments; + rawResults_t dataResults; +}; + +} //namespace common +} //nmasepace Fri +} //namespace Protocols +} //namespace libstark + +#endif // #ifndef RS_IOPP_BIASED_COMMON diff --git a/libstark/src/protocols/Fri/prover.cpp b/libstark/src/protocols/Fri/prover.cpp new file mode 100644 index 0000000..444812a --- /dev/null +++ b/libstark/src/protocols/Fri/prover.cpp @@ -0,0 +1,346 @@ +#include "prover.hpp" +#include "common/Utils/TaskReporting.hpp" +#include "algebraLib/SubspacePolynomial.hpp" + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace Prover{ + +using common::subproofLocation_t; +using common::state_t; +using common::rawQueries_t; +using common::rawResults_t; +using common::getL0Basis; +using common::getL1Basis; +using common::getBasisLIndex_byL0L1indices; +using common::getColumnBasis; +using common::verifierRequest_t; +using common::proverResponce_t; +using CryptoCommitment::hashDigest_t; +using CryptoCommitment::SparceMerkleTree; +using Infrastructure::Log2; +using Infrastructure::POW2; +using Algebra::PolynomialDegree; +using Algebra::UnivariatePolynomialGeneral; +using Algebra::zero; +using Algebra::FieldElement; +using Algebra::getSpaceElementByIndex; +using std::unique_ptr; +using std::vector; +using std::max; +using std::ceil; + +namespace{ + +struct queryInfo_t{ + const dataWithCommitment* proof; + const rawQuery_t* rawQueries; + vector* results; + size_t depth; + + queryInfo_t( + const dataWithCommitment& proof_, + const rawQuery_t& rawQueries_, + vector& results_, + const size_t depth_): + proof(&proof_), + rawQueries(&rawQueries_), + results(&results_), + depth(depth_){}; +}; + +vector serializeQueries( + const state_t>& currState, + const rawQueries_t& rawQueries, + state_t>& rawResults, + const size_t depth){ + + vector SerialQueries; + + // + // Serialize local queries + // + { + if(!rawQueries.localState.empty()){ + SerialQueries.push_back(queryInfo_t(*currState.localState, rawQueries.localState, rawResults.localState, depth)); + } + } + + // + // Serialize subproof queries + // + for(const auto& q : rawQueries.subproofs){ + const auto subproofQueries = + serializeQueries(currState.subproofs.at(q.first), rawQueries.subproofs.at(q.first), rawResults.subproofs[q.first], depth+1); + + SerialQueries.insert(SerialQueries.end(),subproofQueries.begin(),subproofQueries.end()); + } + + return SerialQueries; +} + +hashDigest_t addSubproof( + state_t>& currState, + const vector& evaluationBasis, + const subproofLocation_t& pathToProof, + const subproofLocation_t& pathFromRoot, + const bool L0isMSB + ){ + + // + // Base case of recursion + // + if(pathToProof.size() == 0){ + return currState.localState->getCommitment(); + } + + // + // Updating paths + // + const auto& currWay = pathToProof[0]; + const subproofLocation_t nextPathToProof(pathToProof.begin()+1, pathToProof.end()); + subproofLocation_t nextPathFromRoot(1,currWay); + nextPathFromRoot.insert(nextPathFromRoot.end(),pathFromRoot.begin(),pathFromRoot.end()); + + // + // Basis of next evaluation + // + const vector basisForColumnsProof(getColumnBasis(evaluationBasis,L0isMSB)); + + // + // Check if current univariate already evaluated, and evaluate if needed + // + if(currState.subproofs.count(currWay) == 0){ + const vector BasisL0 = getL0Basis(evaluationBasis,L0isMSB); + const vector BasisL1 = getL1Basis(evaluationBasis,L0isMSB); + + const unsigned short logSigmentLen = getL0Basis(basisForColumnsProof,L0isMSB).size(); + const unsigned short logNumSigments = getL1Basis(basisForColumnsProof,L0isMSB).size(); + const unsigned short logSigmentsInBlock = std::min((unsigned short)10,logNumSigments); + + const size_t sigmentLen = POW2(logSigmentLen); + const size_t sigmentsInBlock = POW2(logSigmentsInBlock); + + /// + /// The following is a trick for faster evaluation + /// + /// We have : values of a polynomial over a space L_0 + /// We want : the polynomials value over another point x not in L_0 + /// + /// the Lagrange polynomial for \alpha \in L_0 is: + /// + /// l_\alpha (x) = + /// \frac{ \prod_{\beta \ne \alpha \in L_0} (x - \beta) }{ \prod_{\beta \ne \alpha \in L_0} (\alpha - \beta) } = + /// \frac{ Z_{L_0}(x) }{ (x-\alpha) \cdot \prod_{\beta \ne \alpha \in L_0} (\alpha - \beta) } + /// + /// We Define: + /// + /// C_\alpha := \prod_{\beta \ne \alpha \in L_0} (\alpha - \beta) + /// + /// Thus, given values p(\alpha) for any \alpha in L_0, the value over $x$ is: + /// + /// p(x) = + /// \sum_{\alpha \in L_0} p(\alpha) \cdot l_\alpha (x) = + /// Z_{L_0} (x) \cdot \sum_{\alpha \in L_0} \frac{ p(\alpha) }{(x-\alpha) \cdot C_\alpha} + /// + /// In this formula many factors are independent of $x$ and can be precomputed, and this is what used bellow + /// + + // global auxiliary values + const size_t L0_size = POW2(BasisL0.size()); + vector spaceElements(L0_size); + for(unsigned int i=0; i C_alpha(L0_size,Algebra::one()); + { + + for(unsigned int i=0; i vecToInveresePointwise(sigmentsInBlock*sigmentLen*L0_size); + + for(unsigned int localSigmentIdx = 0; localSigmentIdx < sigmentsInBlock; localSigmentIdx++){ + const size_t sigmentIdx = sigmentsBlockIdx*sigmentsInBlock + localSigmentIdx; + + for(unsigned int i=0; i< sigmentLen; i++){ + const size_t globalIndex = sigmentIdx * sigmentLen + i; + const FieldElement currOffset = getSpaceElementByIndex(BasisL1,zero(),globalIndex); + + for(size_t j=0; j< L0_size; j++){ + const FieldElement alpha = spaceElements[j]; + const size_t elementIndex = localSigmentIdx*sigmentLen*L0_size + i*L0_size + j; + vecToInveresePointwise[elementIndex] = ((currWay+currOffset)-alpha)*C_alpha[j]; + } + } + } + + const vector denuminators = Algebra::invertPointwise(vecToInveresePointwise); + + for(unsigned int localSigmentIdx = 0; localSigmentIdx < sigmentsInBlock; localSigmentIdx++){ + const size_t sigmentIdx = sigmentsBlockIdx*sigmentsInBlock + localSigmentIdx; + FieldElement* currSigRes = res + localSigmentIdx*sigmentLen; + + for(unsigned int i=0; i< sigmentLen; i++){ + const size_t globalIndex = sigmentIdx * sigmentLen + i; + const FieldElement currOffset = getSpaceElementByIndex(BasisL1,zero(),globalIndex); + + currSigRes[i] = Algebra::zero(); + for(size_t j=0; j< L0_size; j++){ + const size_t currElemIdx = getBasisLIndex_byL0L1indices(evaluationBasis,j,globalIndex,L0isMSB); + const FieldElement alpha = spaceElements[j]; + const FieldElement currVal = currState.localState->getElement(currElemIdx); + + const size_t elementIndex = localSigmentIdx*sigmentLen*L0_size + i*L0_size + j; + currSigRes[i] += currVal * denuminators[elementIndex]; + } + currSigRes[i] *= Z_L0.eval(currWay+currOffset); + } + } + }; + + currState.subproofs[currWay].localState = + unique_ptr( + new dataWithCommitment( + logSigmentLen + logSigmentsInBlock, + logNumSigments - logSigmentsInBlock, + sigmentConstructor + ) + ); + } + + + // + // Continue recursively + // + return addSubproof(currState.subproofs[currWay], basisForColumnsProof, nextPathToProof, nextPathFromRoot,L0isMSB); + } +} + +prover_t::prover_t(const std::vector& evaluationBasis, std::vector&& evaluation, const bool L0isMSB): + evaluationBasis_(evaluationBasis), + L0isMSB_(L0isMSB) + { + + // + //construct the root evaqluation + // + { + TASK("Constructing proof of proximity root"); + state_.localState = unique_ptr(new dataWithCommitment(std::move(evaluation), getL0Basis(evaluationBasis,L0isMSB_).size())); + } +} + +void prover_t::receiveMessage(const TranscriptMessage& msg){ + const verifierRequest_t& vMsgCast = dynamic_cast(msg); + + //retrieve commitments + { + nextResponce_.proofConstructionComitments = constructProofs(vMsgCast.proofConstructionQueries); + } + //retrieve data + { + nextResponce_.dataResults = responceToDataQueries(vMsgCast.dataQueries); + } +} + +msg_ptr_t prover_t::sendMessage(){ + return msg_ptr_t(new proverResponce_t(nextResponce_)); +} + + +void prover_t::deleteProof(){ +#if __GNUG__ + state_ = common::state_t>(); +#endif +} + +vector prover_t::constructProofs(const vector& proofsList){ + + // + // Start by sorting requests by layers + // + map> requestsByLayer; + { + for(unsigned int i=0; i < proofsList.size(); i++){ + const size_t currPathLenght = proofsList[i].size(); + requestsByLayer[currPathLenght].push_back(i); + } + } + + //initialize the results vector + vector result(proofsList.size()); + + // + // First comitment for root, if was requested + // + { + if(requestsByLayer.count(0) > 0){ + const auto rootCommitment = state_.localState->getCommitment(); + for(const size_t resIdx : requestsByLayer.at(0)){ + result[resIdx] = rootCommitment; + } + } + } + + // + // Now continue to the rest + // + for(const auto& layer : requestsByLayer){ + if(layer.first == 0){ + continue; + } + + TASK("Constructing layer #" + std::to_string(layer.first) + " of proof of proximity"); + + for(unsigned int i=0; i < layer.second.size(); i++){ + const auto& reqIdx = layer.second[i]; + result[reqIdx] = addSubproof(state_, evaluationBasis_, proofsList[reqIdx], subproofLocation_t(), L0isMSB_); + } + } + + return result; +} + +rawResults_t prover_t::responceToDataQueries(const rawQueries_t& queries)const{ + TASK("Answering data queries from proof of proximity"); + + rawResults_t results; + + { + auto serialQueries = serializeQueries(state_, queries, results, 0); + +#pragma omp parallel for + for(unsigned int i=0; ianswerQueries(*currQ.rawQueries); + *currQ.results = currTree.toVector(); + } + } + + return results; +} + + +} //namespace Prover +} //nmasepace Fri +} //namespace Protocols +} //namespace libstark diff --git a/libstark/src/protocols/Fri/prover.hpp b/libstark/src/protocols/Fri/prover.hpp new file mode 100644 index 0000000..f6a1393 --- /dev/null +++ b/libstark/src/protocols/Fri/prover.hpp @@ -0,0 +1,36 @@ +#ifndef RS_IOPP_BIASED_PROVER_HPP__ +#define RS_IOPP_BIASED_PROVER_HPP__ + +#include "common/common.hpp" +#include "protocols/common/proofs.hpp" + +#include + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace Prover{ + +class prover_t : public PartieInterface{ +public: + prover_t(const std::vector& evaluationBasis, std::vector&& evaluation, const bool L0isMSB); + void receiveMessage(const TranscriptMessage& msg); + msg_ptr_t sendMessage(); + void deleteProof(); +private: + const std::vector evaluationBasis_; + common::state_t> state_; + common::proverResponce_t nextResponce_; + const bool L0isMSB_; + + //methods + std::vector constructProofs(const std::vector& proofsList); + common::rawResults_t responceToDataQueries(const common::rawQueries_t& queries)const; +}; + +} //namespace Prover +} //nmasepace Fri +} //namespace Protocols +} //namespace libstark + +#endif // #ifndef RS_IOPP_BIASED_PROVER_HPP__ diff --git a/libstark/src/protocols/Fri/verifier.cpp b/libstark/src/protocols/Fri/verifier.cpp new file mode 100644 index 0000000..761d7cc --- /dev/null +++ b/libstark/src/protocols/Fri/verifier.cpp @@ -0,0 +1,394 @@ +#include "verifier.hpp" +#include "protocols/common/CryptoCommitment/MerkleCommitment.hpp" +#include "common/Utils/TaskReporting.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include "common/Utils/ErrorHandling.hpp" + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace Verifier{ + +using std::vector; +using std::pair; +using std::unique_ptr; +using Algebra::FieldElement; +using CryptoCommitment::hashDigest_t; +using CryptoCommitment::SparceMerkleTree; +using details::RS_queriesTree; +using details::addRandomComitmentPath; +using details::addRandomQueryPath; + +using common::dimOfColumn; + +using common::state_t; +using common::rawQueries_t; +using common::rawResults_t; +using common::verifierRequest_t; +using common::proverResponce_t; +using common::subproofLocation_t; + +using Infrastructure::Log2; + +namespace{ + + size_t expectedQueriedFieldElementsNum(const state_t& src){ + size_t res = src.localState.expectedQueriedFieldElementsNum(); + + for(auto& subproof : src.subproofs){ + res += expectedQueriedFieldElementsNum(subproof.second); + } + + return res; + } + + size_t expectedHashesInResults(const state_t& src){ + size_t res = src.localState.expectedResultsLenght(); + + for(auto& subproof : src.subproofs){ + res += expectedHashesInResults(subproof.second); + } + + return res; + } + + size_t expectedProofSize(const state_t& src){ + size_t res = Infrastructure::POW2(src.localState.getLogSizeBytes()); + + for(auto& subproof : src.subproofs){ + res += expectedProofSize(subproof.second); + } + + return res; + } + + + void fill_queries( + const RS_queriesTree& srcQueries, + state_t& dstState, + const size_t dimOfL){ + + //local queries + dstState.localState.view = SparceMerkleTree(dimOfL + Log2(sizeof(FieldElement))); + for(const auto& q : srcQueries.localState){ + + const size_t x = q.first; + const size_t blockIndex = CryptoCommitment::getBlockIndex(x); + const short offsetInBlock = CryptoCommitment::getOffsetInBlock(x); + + auto setValFunc = [=,&q](const FieldElement& res){ q.second.answer(res); }; + dstState.localState.queries[blockIndex][offsetInBlock].push_back(setValFunc); + } + + //child queries + const size_t columnBasisSize = dimOfColumn(dimOfL); + for(const auto& subproofQuerie : srcQueries.subproofs){ + fill_queries(subproofQuerie.second,dstState.subproofs[subproofQuerie.first],columnBasisSize); + } + } + + void getRawQuery_IOPP(rawQueries_t& dst ,const state_t& src){ + dst.localState = src.localState.getRawQuery(); + + for(auto& subproof : src.subproofs){ + getRawQuery_IOPP(dst.subproofs[subproof.first], subproof.second); + } + } + + void digestResults_rec(state_t& dst, const rawResults_t& src){ + dst.localState.digestResults(src.localState); + + for(auto& subproof : dst.subproofs){ + digestResults_rec(subproof.second,src.subproofs.at(subproof.first)); + } + } + + void fillResultsAndCommitmentsRandomly_rec(state_t& dst){ + dst.localState.fillResultsAndCommitmentRandomly(); + + for(auto& subproof : dst.subproofs){ + fillResultsAndCommitmentsRandomly_rec(subproof.second); + } + } + + bool verifyComitment_rec(const state_t& currState){ + bool res = currState.localState.verifyComitment(); + + for(const auto& subproof : currState.subproofs){ + res &= verifyComitment_rec(subproof.second); + } + + return res; + } + + void fetchResults_rec(const state_t& currState){ + + currState.localState.fetchResults(); + + for(const auto& subproof : currState.subproofs){ + fetchResults_rec(subproof.second); + } + } + +} + +const unsigned short calculateRecursionDepth(const unsigned short basisSize, const unsigned short dimReduction, const unsigned short logDegBound){ + //return biggest depth such that any evaluation is at least 256 bits + + const unsigned short depthPoly = floor(float(logDegBound)/float(dimReduction)); + + const unsigned short minSpaceDim = 2+int(CryptoCommitment::logBytesPerHash - Log2(sizeof(FieldElement))); + const unsigned short depthSpace = floor(float(basisSize-minSpaceDim)/float(dimReduction)); + + const unsigned short depth = std::min(depthPoly,depthSpace); + + return depth; +} + +const size_t getNumCommitmentPaths(const unsigned short basisSize, const unsigned short fieldExtensionDim, const unsigned short securityLevel){ + return std::ceil(securityLevel/double(fieldExtensionDim - (basisSize+1))); +} + +const size_t getNumQueryPaths(const unsigned short basisSize, const unsigned short logDegBound, const unsigned short securityLevel){ + return std::ceil(double(securityLevel) / (basisSize-logDegBound)); +} + +verifier_t::verifier_t(const vector evaluationBasis, const unsigned short securityLevel, const unsigned short logDegBound, const bool L0isMSB, specsPrinter& specsCollector): + evaluationBasis_(evaluationBasis), + depth_(calculateRecursionDepth(evaluationBasis.size(),common::SoundnessParameters::dimReduction, logDegBound)), + L0isMSB_(L0isMSB), + recievedResults_(false) +{ + + const size_t numQueryPaths = getNumQueryPaths(evaluationBasis_.size(),logDegBound, securityLevel); + const size_t numComitmentPaths = getNumCommitmentPaths(evaluationBasis_.size(),FFF::Element::ord,securityLevel); + + { + using std::to_string; + + specsCollector.addLine("field size (|F|)","2^64"); + specsCollector.addLine("RS code dimension","2^" + to_string(logDegBound)); + specsCollector.addLine("RS block-length","2^" + to_string(evaluationBasis.size())); + specsCollector.addLine("RS code rate","2^-{" + to_string(evaluationBasis.size()-logDegBound) + "}"); + specsCollector.addLine("Soundness error","2^-{" + to_string(securityLevel) + "}"); + specsCollector.addLine("dim L_0 (eta)",to_string(common::SoundnessParameters::dimReduction)); + specsCollector.addLine("recursion depth",to_string(depth_)); + specsCollector.addLine("COMMIT repetitions",to_string(numComitmentPaths)); + specsCollector.addLine("number of tests (ell)",to_string(numQueryPaths)); + } + + TASK("Generating Reed Solomon Proximity proofs queries to reach security level of " + + std::to_string(securityLevel) + + " bits, for evaluation over a space of dimention " + + std::to_string(evaluationBasis.size())); + + // + // Construct queries + // + { + // construct comitment paths and link to results + { + TASK("Generating " + std::to_string(numComitmentPaths) + " commites query paths of depth " + std::to_string(depth_)); + + RS_results_.resize(numComitmentPaths); + for(size_t i=0; i< numComitmentPaths; i++){ + addRandomComitmentPath(logDegBound, evaluationBasis_ , const_cast(queries_), RS_results_[i], depth_, L0isMSB_); + } + } + + // constract consistency query paths and link to results + { + TASK("Generating " + std::to_string(numQueryPaths) + " consistency paths queries"); + + Consistency_path_results_.resize(numQueryPaths); + for(size_t i=0; i< numQueryPaths; i++){ + addRandomQueryPath(const_cast(queries_), evaluationBasis_ , Consistency_path_results_[i], L0isMSB_); + } + } + + // fetch queries for univariate for external verifier + { + for(const auto& q : queries_.univariate){ + const size_t x = q.first; + auto& currLocationsList = const_cast(queriesToInput_)[x]; + currLocationsList.insert(currLocationsList.end(),q.second.begin(),q.second.end()); + } + } + + // fetch queries for bivariates and store in verifier state + { + fill_queries(queries_.proof, state_, evaluationBasis_.size()); + } + } + + // + // Construct first layer + // + { + //small hack - this line 'asks' first for the 0 layer (the linear combinatoin) + currProofRequests_.push_back( proofRequest_t(&state_,subproofLocation_t(0))); + + //now asks for the next layer + recieveProofConstructionComitments(vector(1)); + } +} + +rawQueries_t verifier_t::getRawQueries()const{ + rawQueries_t result; + getRawQuery_IOPP(result , state_); + return result; +} + +void verifier_t::digestResults(const rawResults_t& results){ + digestResults_rec(state_,results); +} + +void verifier_t::fillResultsAndCommitmentRandomly(){ + fillResultsAndCommitmentsRandomly_rec(state_); +} + +void verifier_t::receiveMessage(const TranscriptMessage& msg){ + const proverResponce_t& responce = dynamic_cast(msg); + + if(!doneConstructingProof()){ + //Still constructing the proof + recieveProofConstructionComitments(responce.proofConstructionComitments); + } + else{ + //Those must be the data queries results + digestResults(responce.dataResults); + fetchResults(); + recievedResults_ = true; + } +} + +msg_ptr_t verifier_t::sendMessage(){ + verifierRequest_t* request(new verifierRequest_t()); + + if(!doneConstructingProof()){ + //Still constructing proof + request->proofConstructionQueries = getCurrProofConstructionRequest(); + } + else{ + //The only thing left to do is to query th data + request->dataQueries = getRawQueries(); + } + + return msg_ptr_t(request); +} + +bool verifier_t::verifyComitment()const{ + TASK("Verifying integrity with commitment"); + return verifyComitment_rec(state_); +} + +void verifier_t::fetchResults(){ + fetchResults_rec(state_); +} + +bool verifier_t::verifyLowDegree()const{ + bool res = true; + + { + TASK("verifying Reed-Solomon proximity proof : " + std::to_string(RS_results_.size()) + " low degree explicit tests"); + bool localRes = true; + for(const auto& res : RS_results_){ + localRes &= res.verify(); + } + + if(localRes == false){ + res = false; + } + } + + return res; +} + +bool verifier_t::verifyConsistencyPaths()const{ + bool res = true; + + { + TASK("Verifying Consistency Paths : " + std::to_string(Consistency_path_results_.size()) + " paths"); + for (const auto& path : Consistency_path_results_){ + for (const auto& pRes : path){ + res &= pRes.verify(); + } + } + } + + return res; +} + +bool verifier_t::verify()const{ + return verifyComitment() & verifyLowDegree() & verifyConsistencyPaths(); +} + +const queriesToInp_t& verifier_t::queriesToInput()const{ + return queriesToInput_; +} +bool verifier_t::doneInteracting()const{ + return recievedResults_; +} + +bool verifier_t::doneConstructingProof()const{ + return currProofRequests_.empty(); +} + +vector verifier_t::getCurrProofConstructionRequest()const{ + + vector currReq; + for(const auto& p : currProofRequests_){ + currReq.push_back(p.second); + } + + return currReq; +} + +void verifier_t::recieveProofConstructionComitments(const vector& comitments){ + // + // Keep the commitments + // + + { + _COMMON_ASSERT(currProofRequests_.size() == comitments.size(), "Number of commitments not as expected"); + + for(size_t i=0; i< currProofRequests_.size(); i++){ + currProofRequests_[i].first->localState.commitment = comitments[i]; + } + } + + // + // prepare for next layer + // + { + vector nextRequests; + for(const auto& p : currProofRequests_){ + const auto& currPath = p.second; + + for(auto& subproofNode : p.first->subproofs){ + auto newPath = currPath; + newPath.push_back(subproofNode.first); + nextRequests.push_back(proofRequest_t(&subproofNode.second, newPath)); + } + } + + currProofRequests_ = nextRequests; + } +} + +size_t verifier_t::expectedCommitedProofBytes()const{ + return expectedProofSize(state_); +} + +size_t verifier_t::expectedSentProofBytes()const{ + return expectedHashesInResults(state_) * sizeof(hashDigest_t); +} + +size_t verifier_t::expectedQueriedDataBytes()const{ + return expectedQueriedFieldElementsNum(state_) * sizeof(FieldElement); +} + +} //namespace Verifier +} //nmasepace Fri +} //namespace Protocols +} //namespace libstark diff --git a/libstark/src/protocols/Fri/verifier.hpp b/libstark/src/protocols/Fri/verifier.hpp new file mode 100644 index 0000000..8be7747 --- /dev/null +++ b/libstark/src/protocols/Fri/verifier.hpp @@ -0,0 +1,62 @@ +#ifndef RS_IOPP_BIASED_VERIFIER_HPP__ +#define RS_IOPP_BIASED_VERIFIER_HPP__ + +#include "verifier_details/queryGenerator.hpp" +#include "common/common.hpp" +#include "protocols/common/verifier.hpp" +#include "common/Utils/specsPrint.hpp" + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace Verifier{ + +class verifier_t : public IOPP_verifierInterface{ +public: + verifier_t(const std::vector evaluationBasis, const unsigned short securityLevel, const unsigned short logDegBound, const bool L0isMSB, specsPrinter& specsCollector); + void receiveMessage(const TranscriptMessage& msg); + msg_ptr_t sendMessage(); + bool doneInteracting()const; + const queriesToInp_t& queriesToInput()const; + bool verify()const; + + size_t expectedCommitedProofBytes()const; + size_t expectedSentProofBytes()const; + size_t expectedQueriedDataBytes()const; + + void fillResultsAndCommitmentRandomly(); + +private: + const std::vector evaluationBasis_; + const queriesToInp_t queriesToInput_; + const size_t depth_; + const bool L0isMSB_; + + const details::RS_queries queries_; + std::vector RS_results_; + std::vector Consistency_path_results_; + + bool recievedResults_; + + typedef std::pair*, common::subproofLocation_t> proofRequest_t; + std::vector currProofRequests_; + common::state_t state_; + + //methods + std::vector getCurrProofConstructionRequest()const; + void recieveProofConstructionComitments(const std::vector& comitments); + bool doneConstructingProof()const; + common::rawQueries_t getRawQueries()const; + void digestResults(const common::rawResults_t& results); + void fetchResults(); + bool verifyComitment()const; + bool verifyLowDegree()const; + bool verifyConsistencyPaths()const; +}; + +} //namespace Verifier +} //nmasepace RS_IOPP +} //namespace Protocols +} //namespace libstark + +#endif //#ifndef RS_IOPP_BIASED_VERIFIER_HPP__ diff --git a/libstark/src/protocols/Fri/verifier_details/queryGenerator.cpp b/libstark/src/protocols/Fri/verifier_details/queryGenerator.cpp new file mode 100644 index 0000000..80580ca --- /dev/null +++ b/libstark/src/protocols/Fri/verifier_details/queryGenerator.cpp @@ -0,0 +1,263 @@ +#include "queryGenerator.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include "common/Utils/ErrorHandling.hpp" +#include "common/Utils/TaskReporting.hpp" +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace Verifier{ +namespace details{ + +//using common::dimOfLBeta; +using common::getL1Basis; +using common::getL0Basis; +using common::getColumnBasis; +using common::getBasisLIndex_byL0L1indices; + +using Infrastructure::POW2; +using Algebra::FieldElement; +using Algebra::zero; +using std::vector; +using std::pair; +using std::max; + +void RS_result::init(const vector& basis, const size_t degBound){ + results.resize(POW2(basis.size())); + basis_ = basis; + degBound_ = degBound; +} + +//Special cases for degrees +namespace{ +bool verifyDeg0(const vector& results){ + bool res = true; + for(const FieldElement& y : results){ + if(y != Algebra::zero()){ + res = false; + } + } + return res; +} + +bool verifyDeg1(const vector& results){ + bool res = true; + const FieldElement& c = results[0]; + for(const FieldElement& y : results){ + if(y != c){ + res = false; + } + } + return res; +} + +bool verifyDeg2(const vector& results, const unsigned int dim){ + bool res = true; + + //the function should be of the form y=ax+b + const FieldElement& b = results[0]; + int mask = 2; + for(unsigned int i=1; i< dim; i++){ + for(int j=0; j& basis, const FieldElement& columnId){ + L0Data.resize(POW2(basis.size())); + basis_ = basis; + columnId_ = columnId; + + lagrangeConstants_.resize(POW2(basis.size())); + //Init Lagrange constants + { + vector spaceVals(POW2(basis_.size())); + spaceVals[0]=Algebra::zero(); + for(unsigned int i=0; i enumerator(spaceVals.size(),Algebra::one()); + vector denumerator(spaceVals.size(),Algebra::one()); + for(unsigned int i=0; i< L0Data.size(); i++){ + for(unsigned int j=0; j& evaluationBasis, RS_queriesTree& queries, RS_result& results, const size_t depth, const bool L0isMSB){ + + // + //Global constants + // + const size_t MIN_DEPTH = 0; + + // + // If in minimal depth, query the polynomial + // + if(depth <= MIN_DEPTH){ + results.init(evaluationBasis,POW2(degBound_logCeil)); + for(size_t i=0; i< POW2(evaluationBasis.size()); i++){ + auto query_iter = queries.localState.insert(pair(i,ResultLocation())).first; + query_iter->second.addAnswerPtr(&(results.results[i])); + } + return; + } + + // + // recursive call + // + const vector basisForColumnsProof = getColumnBasis(evaluationBasis, L0isMSB); + const short curr_degree_log=max(0,short(basisForColumnsProof.size())-short(evaluationBasis.size()-degBound_logCeil)); + const FieldElement columnId = Algebra::generateRandom(); + addRandomComitmentPath(curr_degree_log,basisForColumnsProof,queries.subproofs[columnId],results,depth-1, L0isMSB); +} + +FieldElement getSubproofKeyByIndex(const RS_queriesTree& t, const size_t idx){ + size_t i =0; + for(const auto& e : t.subproofs){ + if(i++ == idx){ + return e.first; + } + } + + _COMMON_FATAL("Index to large"); +} + +void addRandomQueryPath(RS_queriesTree& commitedTree, + std::map>& univariate, + const vector& evaluationBasis, + const size_t sigmentIdx, + Consistency_path_t& results, const size_t depth, const bool L0isMSB){ + + // the base case, got to deepest point + if(commitedTree.subproofs.empty()){ + results.resize(depth); + return; + } + + // draw a random commited column + const size_t columnIndex = std::rand() % commitedTree.subproofs.size(); + const FieldElement columnId = getSubproofKeyByIndex(commitedTree, columnIndex); + + // calculate curr sigment data + const auto currSigmentBasis = getL0Basis(evaluationBasis, L0isMSB); + const auto L1Basis = getL1Basis(evaluationBasis, L0isMSB); + const FieldElement currSigmentOffset = getSpaceElementByIndex(L1Basis, zero(), sigmentIdx); + + // call recursivly + // do it befor initializing results to keep there address in memory uncahned + // (as the size of the results vector is yet unknown) + { + const auto nextColumnBasis = getColumnBasis(evaluationBasis, L0isMSB); + const auto nextSigmentDim = getL0Basis(nextColumnBasis, L0isMSB).size(); + const size_t nextSigmentIdx = sigmentIdx >> nextSigmentDim; + + addRandomQueryPath(commitedTree.subproofs[columnId], univariate, nextColumnBasis, nextSigmentIdx, results, depth+1, L0isMSB); + } + + // initialize queries and results + { + auto& currRes = results[depth]; + currRes.init(currSigmentBasis, columnId - currSigmentOffset); + + commitedTree.subproofs[columnId].localState[sigmentIdx].addAnswerPtr(&currRes.columnPointData); + + const size_t currSigmentSize = POW2(currSigmentBasis.size()); + for(size_t i=0; i< currSigmentSize; i++){ + const size_t currGlobalIdx = getBasisLIndex_byL0L1indices(evaluationBasis,i,sigmentIdx, L0isMSB); + if(depth > 0){ + commitedTree.localState[currGlobalIdx].addAnswerPtr(&currRes.L0Data[i]); + } + else{ + univariate[currGlobalIdx].push_back(&currRes.L0Data[i]); + } + } + } +} + +} + +void addRandomComitmentPath(const short degBound_logCeil, const vector& evaluationBasis, RS_queries& queries, RS_result& results, const size_t depth, const bool L0isMSB){ + addRandomComitmentPath(degBound_logCeil, evaluationBasis, queries.proof, results, depth, L0isMSB); +} + +void addRandomQueryPath(RS_queries& queries, const vector& evaluationBasis, Consistency_path_t& results, const bool L0isMSB){ + const size_t numOfSigments = POW2(getL1Basis(evaluationBasis, L0isMSB).size()); + const size_t sigmentIdx = rand() % numOfSigments; + addRandomQueryPath(queries.proof,queries.univariate,evaluationBasis, sigmentIdx, results, 0, L0isMSB); +} + +} //namespace details +} //namespace Verifier +} //nmasepace Fri +} //namespace Protocols +} //namespace libstark diff --git a/libstark/src/protocols/Fri/verifier_details/queryGenerator.hpp b/libstark/src/protocols/Fri/verifier_details/queryGenerator.hpp new file mode 100644 index 0000000..728196c --- /dev/null +++ b/libstark/src/protocols/Fri/verifier_details/queryGenerator.hpp @@ -0,0 +1,65 @@ +#ifndef RS_IOPP_BIASED_DETAILS_QUERYGEN_HPP__ +#define RS_IOPP_BIASED_DETAILS_QUERYGEN_HPP__ + +#include "../common/common.hpp" +#include "protocols/common/verifier.hpp" +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace Fri{ +namespace Verifier{ +namespace details{ + +class RS_result{ +public: + std::vector results; + + void init(const std::vector& basis, const size_t degBound); + bool verify()const; +private: + std::vector basis_; + size_t degBound_; +}; + +class ConsistencyPath_result{ +public: + std::vector L0Data; + Algebra::FieldElement columnPointData; + + void init(const std::vector& basis, const Algebra::FieldElement& columnId); + bool verify()const; +private: + std::vector basis_; + Algebra::FieldElement columnId_; + std::vector lagrangeConstants_; +}; + +class equavalenceQuery_result{ +public: + Algebra::FieldElement res1; + Algebra::FieldElement res2; + bool verify()const{return res1 == res2;} +}; + +typedef std::vector Consistency_path_t; + +typedef common::state_t> RS_queriesTree; + +struct RS_queries{ + RS_queriesTree proof; + std::map> univariate; +}; + +void addRandomComitmentPath(const short degBound_logCeil, const std::vector& evaluationBasis, RS_queries& queries, RS_result& results, const size_t depth, const bool L0isMSB); + +void addRandomQueryPath(RS_queries& queries, const std::vector& evaluationBasis, Consistency_path_t& results, const bool L0isMSB); + +} //namespace details +} //namespace Verifier +} //nmasepace Fri +} //namespace Protocols +} //namespace libstark + +#endif //#ifndef RS_IOPP_BIASED_DETAILS_QUERYGEN_HPP__ diff --git a/libstark/src/protocols/common/CryptoCommitment/MerkleCommitment.cpp b/libstark/src/protocols/common/CryptoCommitment/MerkleCommitment.cpp new file mode 100644 index 0000000..47a4cae --- /dev/null +++ b/libstark/src/protocols/common/CryptoCommitment/MerkleCommitment.cpp @@ -0,0 +1,494 @@ +#include "MerkleCommitment.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include "common/Utils/ErrorHandling.hpp" +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif // #ifdef WIN32 +#ifdef __GNUC__ +#include +#endif // #ifdef __GNUC__ + +namespace libstark{ +namespace Protocols{ +namespace CryptoCommitment{ + +using std::vector; + +//AES128 hash specific functions +namespace{ + //macros + +#define AES_128_key_exp(k, rcon) aes_128_key_expansion(k, _mm_aeskeygenassist_si128(k, rcon)) + + +static __m128i aes_128_key_expansion(__m128i key, __m128i keygened){ + keygened = _mm_shuffle_epi32(keygened, _MM_SHUFFLE(3,3,3,3)); + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + return _mm_xor_si128(key, keygened); +} + +//encrypts the data +inline __m128i aes128_enc(const __m128i& data, const __m128i& enc_key){ + __m128i m = data; + + __m128i k0 = enc_key; + m = _mm_xor_si128 (m, k0); + + __m128i k1 = AES_128_key_exp(k0, 0x01); + m = _mm_aesenc_si128 (m, k1); + + __m128i k2 = AES_128_key_exp(k1, 0x02); + m = _mm_aesenc_si128 (m, k2); + + __m128i k3 = AES_128_key_exp(k2, 0x04); + m = _mm_aesenc_si128 (m, k3); + + __m128i k4 = AES_128_key_exp(k3, 0x08); + m = _mm_aesenc_si128 (m, k4); + + __m128i k5 = AES_128_key_exp(k4, 0x10); + m = _mm_aesenc_si128 (m, k5); + + __m128i k6 = AES_128_key_exp(k5, 0x20); + m = _mm_aesenc_si128 (m, k6); + + __m128i k7 = AES_128_key_exp(k6, 0x40); + m = _mm_aesenc_si128 (m, k7); + + __m128i k8 = AES_128_key_exp(k7, 0x80); + m = _mm_aesenc_si128 (m, k8); + + __m128i k9 = AES_128_key_exp(k8, 0x1B); + m = _mm_aesenc_si128 (m, k9); + + __m128i k10 = AES_128_key_exp(k9, 0x36); + m = _mm_aesenclast_si128(m, k10); + + return m; +} +} + +//hashes 64 bytes from src into 32 bytes in dst +void hash(void const* const src, void * const dst){ + + // + //Code for SHA-256 + // + + //SHA256_CTX sha256; + //SHA256_Init(&sha256); + //SHA256_Update(&sha256,src,hash_src_len); + //SHA256_Final((unsigned char*)dst,&sha256); + + // + //Code for AES-128 based hash + // + + const __m128i key = _mm_loadu_si128((__m128i*)src); + const __m128i plaintext = _mm_loadu_si128(((__m128i*)src)+1); + const __m128i encRes = aes128_enc(plaintext, key); + + _mm_storeu_si128((__m128i*)dst, _mm_xor_si128(encRes,plaintext)); +} + +hashDigest_t hash(void const* const src){ + hashDigest_t res; + hash(src,&res); + return res; +} + +std::string hashDigest_t::toString()const{ + std::stringstream stream; + stream << std::hex; + + short* shortsBuff = (short*)&buffer; + for(unsigned short i=0; i< sizeof(hashDigest_t)/sizeof(short); i++){ + stream << std::setfill('0')<< std::setw(sizeof(short)*2) << shortsBuff[i] <<" "; + } + + return stream.str(); +} + +bool operator==(const hashDigest_t& a, const hashDigest_t& b){ + return 0 == std::memcmp(&a,&b,sizeof(hashDigest_t)); +} + +bool operator!=(const hashDigest_t& a, const hashDigest_t& b){ + return !(a==b); +} + +bool operator<(const hashDigest_t& a, const hashDigest_t& b){ + return 0 < std::memcmp(&a,&b,sizeof(hashDigest_t)); +} +unsigned short getBlockSize(){ + return (1U< logBytesPerHash,"It is assumed the source length contains a power of 2 blocks of 256 bits, src_logLen = " + std::to_string(src_logLen)); + + while (curr_dst_logLen >= 0){ + hashDigest_t* curr_dst = ((hashDigest_t*)dst) + (1UL< logBytesPerHash,"It is assumed the source length contains a power of 2 blocks of 256 bits, src_logLen = " + std::to_string(src_logLen)); + + + const unsigned short logLen = src_logLen - logBytesPerHash; + const size_t buffLen = POW2(logLen); + hashDigest_t* data = (hashDigest_t*)dataInp; + + + for (unsigned short currStepLog=1; currStepLog <= logLen; currStepLog++){ + const size_t currStep = POW2(currStepLog); + const size_t prevStep = POW2(currStepLog-1); +#pragma omp parallel for + for(unsigned long long i=0; i logBytesPerHash,"It is assumed the source length contains a power of 2 blocks of 256 bits"); + + while (curr_sigment_logLen >=0){ + hashDigest_t* curr_dst_row = ((hashDigest_t*)dst) + POW2(curr_dst_row_logLen); + hashDigest_t* curr_dst_shifted = curr_dst_row + POW2(curr_sigment_logLen)*sigment_index; + const size_t curr_sigment_len = POW2(curr_sigment_logLen); +#pragma omp parallel for + for(unsigned long long i=0; i>1); + + while(curr_offset > 1UL){ + result.push_back(tree_[curr_offset ^ 1UL]); + curr_offset >>= 1; + } + + return result; +} + +vector getPathToBlocksInPlace(void * dataInp, const short src_logLen, const vector& blockIndices){ + using Infrastructure::POW2; + _COMMON_ASSERT(src_logLen > logBytesPerHash,"It is assumed the source length contains a power of 2 blocks of 256 bits, src_logLen = " + std::to_string(src_logLen)); + + + const unsigned short logLen = src_logLen - logBytesPerHash; + const size_t buffLen = POW2(logLen); + hashDigest_t* data = (hashDigest_t*)dataInp; + + vector res(blockIndices.size()); + + + for (unsigned short currStepLog=1; currStepLog <= logLen; currStepLog++){ + const size_t currStep = POW2(currStepLog); + const size_t prevStep = POW2(currStepLog-1); + + //construct next layer +#pragma omp parallel for + for(unsigned long long i=0; i>(currStepLog-1); + const size_t currTwin = currLayerBlock ^ 1UL; + const size_t TwinLocation = currTwin*currStep; + res[q].push_back(data[TwinLocation]); + } + } + } + + return res; +} + +bool verifyPathToBlock(void const*const blockData, const hashDigest_t& root, const path_t& path, const size_t blockIndex){ + const short firstRow_logLen = path.size(); + size_t curr_offset = (1UL<>1); + + auto currHash = hash(blockData); + + for(size_t i=0; i < path.size(); i++){ + hashDigest_t hash_src[2]; + hash_src[(curr_offset&1UL) ^ 1UL] = path[i]; + hash_src[(curr_offset&1UL)] = currHash; + + currHash = hash(&hash_src); + + curr_offset >>= 1; + } + + //verify root + return currHash == root; +} + +// +// An efficient representation of subtree containing only +// data needed to pass to show consistency of many queried elements +// with the commitment +// +bool SparceMerkleLayer::hasElement(const size_t idx)const{ + return (data_.find(idx) != data_.end()); +} + +void SparceMerkleLayer::addEntry(const size_t idx, const hashDigest_t& data){ + data_[idx] = data; +} + +void SparceMerkleLayer::deleteEntry(const size_t idx){ + data_.erase(data_.find(idx)); +} + +const hashDigest_t& SparceMerkleLayer::readData(const size_t idx)const{ + return data_.at(idx); +} + +hashDigest_t SparceMerkleLayer::hashPair(const size_t idx)const{ + hashDigest_t src[2]; + src[0] = readData(idx<<1); + src[1] = readData((idx<<1)^1UL); + return hash(src); +} + +SparceMerkleLayer SparceMerkleLayer::calculateNextLayer(const SparceMerkleLayer& recieved)const{ + SparceMerkleLayer res = recieved; + + //as it is sufficient to scan only pairs + //we can skip every second entry, + //as it is handled already by it's mate + bool needToCalc = false; + for(const auto& v: data_){ + needToCalc = !needToCalc; + if(!needToCalc){ + continue; + } + const size_t currPairIdx = v.first>>1; + res.addEntry(currPairIdx, hashPair(currPairIdx)); + } + return res; +} + +std::vector SparceMerkleLayer::toVector()const{ + std::vector res; + for( const auto& e : data_){ + res.push_back(e.second); + } + return res; +} + +std::set SparceMerkleLayer::getIndices()const{ + std::set res; + for( const auto& e : data_){ + res.insert(e.first); + } + return res; +} + +SparceMerkleTree::SparceMerkleTree(const short src_logLen){ + const short src_logLenInHashes = src_logLen - logBytesPerHash; + layers_.resize(src_logLenInHashes); +} + +//Serialization +std::vector SparceMerkleTree::toVector()const{ + const auto serializationMapping = getSerializationMapping(layers_[0].getIndices()); + + std::vector res; + for(const auto loc : serializationMapping){ + res.push_back(layers_[loc.first].readData(loc.second)); + } + + return res; +} + +//De serialization +void SparceMerkleTree::DeSerialize(const std::set& queriedIndices, const std::vector& serializedSubtree){ + + const auto serializationMapping = getSerializationMapping(queriedIndices); + _COMMON_ASSERT(serializationMapping.size() == serializedSubtree.size(), + "The serialization length is not as expected : " + std::to_string(serializationMapping.size()) + " != " + std::to_string(serializedSubtree.size())); + for(unsigned int i =0; i < serializationMapping.size(); i++){ + layers_[serializationMapping[i].first].addEntry(serializationMapping[i].second , serializedSubtree[i]); + } +} + +std::vector< std::pair > SparceMerkleTree::getSerializationMapping(const std::set& queriedIndices)const{ + std::vector< std::pair > res; + + //A partial mapping from index that can be calculated + //to wether it is explicit in the serialized subtree (true) + //or implied by lower layer (false) + std::map knownIndices; + + for(const size_t& idx : queriedIndices){ + knownIndices[idx] = true; + knownIndices[idx^1UL] = true; + } + + for(unsigned short layerIdx = 0; layerIdx < layers_.size(); layerIdx++){ + std::map next_knownIndices; + for(const auto& known : knownIndices){ + + if(known.second == true){ + //If current data is explicit in subtree, it must be fetched from serialization + res.push_back(std::pair(layerIdx,known.first)); + } + + //Any case, the hash of current data and its mate + //is computed, thus known implicitly + next_knownIndices[known.first>>1] = false; + } + + //Any element in next known indices that is known must have it's mate + //known as well, thus if it is not known implicitly it must be passed explicitly + //in the serialization + std::vector explictlyKnown; + for(const auto& k : next_knownIndices){ + if (next_knownIndices.count(k.first^1UL) == 0){ + explictlyKnown.push_back(k.first^1UL); + } + } + for(const auto& idx : explictlyKnown){ + next_knownIndices[idx] = true; + } + + //switch the knowledge lists and continue + knownIndices = next_knownIndices; + } + + return res; +} + +void SparceMerkleTree::addPath(const std::array& data, const path_t& path, const size_t pairIdx){ + layers_[0].addEntry(pairIdx<<1,data[0]); + layers_[0].addEntry((pairIdx<<1)^1UL,data[1]); + + size_t currIdx = pairIdx; + for(size_t i=1; i< layers_.size(); i++){ + + //just write the path + layers_[i].addEntry(currIdx^1UL,path[i-1UL]); + + //update the currIdx + currIdx = currIdx>>1; + } +} + +hashDigest_t SparceMerkleTree::calculateRoot()const{ + SparceMerkleLayer currLayer; + + for(const auto& recievedLayer : layers_){ + currLayer = currLayer.calculateNextLayer(recievedLayer); + } + + const hashDigest_t rootCalculated = currLayer.hashPair(0); + + return rootCalculated; +} + +bool SparceMerkleTree::hasData(const size_t idx)const{ + return layers_[0].hasElement(idx); +} + +const hashDigest_t& SparceMerkleTree::readData(const size_t idx)const{ + return layers_[0].readData(idx); +} + +} // namespace CryptoCommitment +} // namespace Protocols +} // namespace libstark diff --git a/libstark/src/protocols/common/CryptoCommitment/MerkleCommitment.hpp b/libstark/src/protocols/common/CryptoCommitment/MerkleCommitment.hpp new file mode 100644 index 0000000..52fa0a0 --- /dev/null +++ b/libstark/src/protocols/common/CryptoCommitment/MerkleCommitment.hpp @@ -0,0 +1,113 @@ +#ifndef MERKLECOMMITMENT_HPP__ +#define MERKLECOMMITMENT_HPP__ + +#include +#include +#include +#include +#include + +namespace libstark{ +namespace Protocols{ +namespace CryptoCommitment{ + +struct hashDigest_t{ + char buffer[128/8]; + + std::string toString()const; +}; + +bool operator==(const hashDigest_t& a, const hashDigest_t& b); +bool operator!=(const hashDigest_t& a, const hashDigest_t& b); +bool operator<(const hashDigest_t& a, const hashDigest_t& b); + +typedef std::vector path_t; + +//hashes 64 bytes from src into 32 bytes in dst +void hash(void const* const src, void * const dst); +hashDigest_t hash(void const* const src); + +const short logBytesPerHash = 4; + +unsigned short getBlockSize(); +unsigned short getDualBlockSize(); +size_t getBlockIndex(const size_t elementIndex); +size_t getElementIndex(const size_t blockIndex); +unsigned short getOffsetInBlock(const size_t index); +unsigned short getOffsetInDualBlock(const size_t index); + +// +// Constructs a Merkle tree for the src buffer (srcLen expected in bytes) +// The tree is written to dst, and its root is returned. +// It is expected src_logLen is in bytes. +// It is expected the size of dst in bytes is at least srcLen. +// +hashDigest_t constructMerkleTree(void const* const src, const short src_logLen, void * const dst); +hashDigest_t getMerkleCommitmentInplace(void * data, const short src_logLen); + +// +// Constructs a Merkle sub-tree for a sigment in the src buffer (srcLen expected in bytes) +// The sub - tree is written to dst +// It is expected src_logLen is in bytes. +// It is expected the size of dst in bytes is at least srcLen. +// +void constructMerkleSubTree(void const* const src, const short src_logLen, const size_t sigment_logLen, const size_t sigment_index, void * const dst); + +path_t getPathToBlock(void const*const tree, const short src_logLen, const size_t blockIndex); +std::vector getPathToBlocksInPlace(void * data, const short src_logLen, const std::vector& blockIndces); + +bool verifyPathToBlock(void const*const blockData, const hashDigest_t& root, const path_t& path, const size_t blockIndex); + +// +// An efficient representation of subtree containing only +// data needed to pass to show consistency of many queried elements +// with the commitment +// +class SparceMerkleLayer{ +public: + bool hasElement(const size_t idx)const; + void addEntry(const size_t idx, const hashDigest_t& data); + void deleteEntry(const size_t idx); + const hashDigest_t& readData(const size_t idx)const; + + //idx is the index of a pair of hashes + hashDigest_t hashPair(const size_t idx)const; + + //calculates the next layer, with merging received data, for verification + SparceMerkleLayer calculateNextLayer(const SparceMerkleLayer& recieved)const; + + std::vector toVector()const; + std::set getIndices()const; +private: + std::map data_; +}; + +class SparceMerkleTree{ +public: + //construct empty sparse tree + // It is expected src_logLen is in bytes. + SparceMerkleTree(const short src_logLen); + + //De serialization + void DeSerialize(const std::set& queriedIndices, const std::vector& serializedSubtree); + + void addPath(const std::array& data, const path_t& path, const size_t pairIdx); + hashDigest_t calculateRoot()const; + + bool hasData(const size_t idx)const; + const hashDigest_t& readData(const size_t idx)const; + + //Serialization + std::vector toVector()const; + + //used to get expected results length + std::vector< std::pair > getSerializationMapping(const std::set& queriedIndices)const; +private: + std::vector layers_; +}; + +} // namespace CryptoCommitment +} // namespace Protocols +} // namespace libstark + +#endif //#ifndef MERKLECOMMITMENT_HPP__ diff --git a/libstark/src/protocols/common/proofs.cpp b/libstark/src/protocols/common/proofs.cpp new file mode 100644 index 0000000..6a01a9e --- /dev/null +++ b/libstark/src/protocols/common/proofs.cpp @@ -0,0 +1,211 @@ +#include "proofs.hpp" +#include "common/Utils/TaskReporting.hpp" +#include "common/Utils/ErrorHandling.hpp" + +namespace libstark{ +namespace Protocols{ + +using CryptoCommitment::hashDigest_t; +using CryptoCommitment::path_t; +using CryptoCommitment::constructMerkleTree; +using CryptoCommitment::logBytesPerHash; +using Infrastructure::Log2; +using Infrastructure::POW2; +using Algebra::FieldElement; +using Algebra::UnivariatePolynomialInterface; +using Algebra::zero; +using std::vector; + +MerkleTree::MerkleTree(FieldElement const*const src, short src_log_size): logSizeBytes_(src_log_size + Log2(sizeof(FieldElement))){ + + tree_ = new hashDigest_t[POW2(logSizeBytes_ - Log2(sizeof(CryptoCommitment::hashDigest_t)))]; + root_ = constructMerkleTree(src,logSizeBytes_,tree_); +} + +MerkleTree::MerkleTree(const vector& src): logSizeBytes_(Log2(src.size()) + Log2(sizeof(CryptoCommitment::hashDigest_t))){ + tree_ = new hashDigest_t[src.size()]; + root_ = constructMerkleTree(&src[0],logSizeBytes_,tree_); +} + +MerkleTree::MerkleTree(short src_log_size): logSizeBytes_(src_log_size+Log2(sizeof(FieldElement))){ + + tree_ = new hashDigest_t[POW2(logSizeBytes_ - Log2(sizeof(CryptoCommitment::hashDigest_t)))]; +} + +void MerkleTree::constructSubtree(FieldElement const*const src, const size_t sigment_logLen, const size_t sigment_index){ + constructMerkleSubTree(src,logSizeBytes_,sigment_logLen + Log2(sizeof(FieldElement)),sigment_index,tree_); +} + +void MerkleTree::finishTreeAfterSegments(const size_t sigment_logLen){ + short currSrcLogLen = logSizeBytes_ - (sigment_logLen + Log2(sizeof(FieldElement))); + if(currSrcLogLen == 0){ + root_ = tree_[1]; + return; + } + root_ = constructMerkleTree(tree_ + POW2(currSrcLogLen), currSrcLogLen + logBytesPerHash, tree_); +} + +const hashDigest_t& MerkleTree::getRoot()const{ + return root_; +} + +path_t MerkleTree::getPathToBlock(const size_t blockIndex)const{ + return CryptoCommitment::getPathToBlock(tree_,logSizeBytes_,blockIndex); +} + +bool MerkleTree::verifyPathToBlock(const path_t& path, FieldElement const*const blockData, const size_t blockIndex)const{ + return CryptoCommitment::verifyPathToBlock(blockData, root_, path, blockIndex); +} + +short MerkleTree::logSizeBytes()const{ + return logSizeBytes_; +} + +MerkleTree::~MerkleTree(){ + delete[] tree_; +} + +namespace{ + + void getFinalHash( + const dataWithCommitment::sigmentConstructor_t& sigmentConstructor, + const unsigned short logNumSigments, + const unsigned short logSigmentLength, + vector& data, + MerkleTree& commitment, + const bool multyThreading){ + + const size_t numSigments = POW2(logNumSigments); + const size_t sigmentLength = POW2(logSigmentLength); + + // + //Calculate buffers + // + if(multyThreading){ +#pragma omp parallel for + for(unsigned long long currSigmentIdx = 0; currSigmentIdx < numSigments; currSigmentIdx++){ + sigmentConstructor(currSigmentIdx,&data[currSigmentIdx*sigmentLength]); + commitment.constructSubtree(&data[0], logSigmentLength, currSigmentIdx); + } + } + else{ + for(unsigned long long currSigmentIdx = 0; currSigmentIdx < numSigments; currSigmentIdx++){ + TASK("Constructing 1 sigment of total " + std::to_string(numSigments)); + + { + TASK("Constructing Data sigment"); + sigmentConstructor(currSigmentIdx,&data[currSigmentIdx*sigmentLength]); + } + { + TASK("Constructing Merkle tree"); + commitment.constructSubtree(&data[0], logSigmentLength, currSigmentIdx); + } + } + } + + commitment.finishTreeAfterSegments(logSigmentLength); + } + + void getUniPolySigment(const UnivariatePolynomialInterface& srcPoly, + const vector& evaluationBasis, const FieldElement& evaluationShift, + const unsigned short degBoundLog, const size_t sigmentIdx, + FieldElement* res){ + // + //Initialize global data relevant to fractions of space for queries + // + const size_t buffDim = degBoundLog; + const vector buffEvalBasis(evaluationBasis.begin(), evaluationBasis.begin()+buffDim); + const vector buffOffsetBasis(evaluationBasis.begin()+buffDim, evaluationBasis.end()); + + // + // Local info for current segment + // + + const FieldElement currOffset = Algebra::getSpaceElementByIndex(buffOffsetBasis,evaluationShift,sigmentIdx); + //TO DO : direct write to res + const auto sigRes = srcPoly.eval(buffEvalBasis,currOffset); + memcpy(res,&sigRes[0],sigRes.size()*sizeof(FieldElement)); + } +} + +UniEvalWithCommitment::UniEvalWithCommitment(const UnivariatePolynomialInterface& srcPoly, +const vector& evaluationBasis, const FieldElement& evaluationShift, +const unsigned short degBoundLog): + dataWithCommitment( + degBoundLog, + evaluationBasis.size() - degBoundLog, + [&](const size_t sigmentIdx, FieldElement* res){ + getUniPolySigment(srcPoly,evaluationBasis,evaluationShift,degBoundLog,sigmentIdx,res); + }), + poly_(srcPoly){}; + +const UnivariatePolynomialInterface& UniEvalWithCommitment::poly()const{return poly_;} + + +dataWithCommitment::dataWithCommitment( + const unsigned short logSigmentLength, + const unsigned short logNumSigments, + const sigmentConstructor_t& sigmentConstructor, + const bool multyThreading): + logSigmentLength_(logSigmentLength), + logNumSigments_(logNumSigments), + data_(POW2(logNumSigments+logSigmentLength)), + commitment_(logNumSigments+logSigmentLength){ + getFinalHash(sigmentConstructor, logNumSigments, logSigmentLength,data_,commitment_,multyThreading); + } + +dataWithCommitment::dataWithCommitment(const vector&& data ,const unsigned short logSigmentLenght): + logSigmentLength_(logSigmentLenght), + logNumSigments_(Log2(data.size())-logSigmentLenght), + data_(data), + commitment_(&data_[0],Log2(data_.size())){}; + +dataWithCommitment::~dataWithCommitment(){ } + +vector dataWithCommitment::getSigment(size_t sigmentId)const{ + const size_t sigBegin = sigmentId*POW2(logSigmentLength_); + const size_t sigEnd = sigBegin + POW2(logSigmentLength_); + vector result(data_.begin() + sigBegin, data_.begin() + sigEnd); + + return result; +} + +const FieldElement& dataWithCommitment::getElement(const size_t index)const{ + _COMMON_ASSERT(index < data_.size(),"Index out of range"); + return data_[index]; +} + +CryptoCommitment::SparceMerkleTree dataWithCommitment::answerQueries(const rawQuery_t& queries)const{ + + CryptoCommitment::SparceMerkleTree resultsTree(logSigmentLength_ + logNumSigments_ + Log2(sizeof(FieldElement))); + + for(const size_t& blockPairIndex : queries){ + const size_t blockIndex = blockPairIndex<<1; + const size_t FElemIndex = CryptoCommitment::getElementIndex(blockIndex); + + std::array data; + { + FieldElement* dataAsFE((FieldElement*)&data[0]); + for(size_t i=0; i< CryptoCommitment::getDualBlockSize(); i++){ + dataAsFE[i] = data_[FElemIndex + i]; + } + } + + const auto currPath = commitment_.getPathToBlock(CryptoCommitment::getBlockIndex(FElemIndex)); + +#pragma omp critical + { + resultsTree.addPath(data, currPath, blockPairIndex); + } + } + + return resultsTree; +} + +CryptoCommitment::hashDigest_t dataWithCommitment::getCommitment()const{ + return commitment_.getRoot(); +} + +} //namespace Protocols +} //namespace libstark + diff --git a/libstark/src/protocols/common/proofs.hpp b/libstark/src/protocols/common/proofs.hpp new file mode 100644 index 0000000..45dd7a7 --- /dev/null +++ b/libstark/src/protocols/common/proofs.hpp @@ -0,0 +1,80 @@ +#ifndef PROTOCOLS_COMMON_PROOFS_HPP__ +#define PROTOCOLS_COMMON_PROOFS_HPP__ + +#include "CryptoCommitment/MerkleCommitment.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include "queries.hpp" +#include +#include +#include +#include + +namespace libstark{ +namespace Protocols{ + +class MerkleTreeInterface{ +public: + virtual const CryptoCommitment::hashDigest_t& getRoot()const = 0; + virtual CryptoCommitment::path_t getPathToBlock(const size_t blockIndex)const = 0; + virtual short logSizeBytes()const = 0; +}; + +class MerkleTree : public MerkleTreeInterface{ +public: + MerkleTree(Algebra::FieldElement const*const src, short src_log_size); + MerkleTree(const std::vector& src); + MerkleTree(short src_log_size); + void constructSubtree(Algebra::FieldElement const*const src, const size_t sigment_logLen, const size_t sigment_index); + void finishTreeAfterSegments(const size_t sigment_logLen); + const CryptoCommitment::hashDigest_t& getRoot()const; + CryptoCommitment::path_t getPathToBlock(const size_t blockIndex)const; + bool verifyPathToBlock(const CryptoCommitment::path_t& path, Algebra::FieldElement const*const blockData, const size_t blockIndex)const; + ~MerkleTree(); + CryptoCommitment::hashDigest_t* tree_; + short logSizeBytes()const; +private: + short logSizeBytes_; + CryptoCommitment::hashDigest_t root_; +}; + +class dataWithCommitment{ +public: + + dataWithCommitment(const std::vector&& data, const unsigned short logSigmentLenght); + + typedef std::function sigmentConstructor_t; + dataWithCommitment(const unsigned short logSigmentLength, const unsigned short logNumSigments, const sigmentConstructor_t& sigmentConstructor, const bool multyThreading = true); + + virtual ~dataWithCommitment(); + + CryptoCommitment::hashDigest_t getCommitment()const; + std::vector getSigment(size_t sigmentId)const; + const Algebra::FieldElement& getElement(const size_t index)const; + CryptoCommitment::SparceMerkleTree answerQueries(const rawQuery_t& queries)const; + const MerkleTree& getMerkleTree()const{return commitment_;} + +private: + const unsigned short logSigmentLength_; + const unsigned short logNumSigments_; + std::vector data_; + MerkleTree commitment_; +}; + +class UniEvalWithCommitment : public dataWithCommitment{ +public: + + UniEvalWithCommitment(const Algebra::UnivariatePolynomialInterface& srcPoly, + const std::vector& evaluationBasis, + const Algebra::FieldElement& evaluationShift, + const unsigned short degBoundLog); + + const Algebra::UnivariatePolynomialInterface& poly()const; + +private: + const Algebra::UnivariatePolynomialGeneral poly_; +}; + +} //namespace Protocols +} //namespace libstark + +#endif // #ifndef PROTOCOLS_COMMON_PROOFS_HPP__ diff --git a/libstark/src/protocols/common/queries.hpp b/libstark/src/protocols/common/queries.hpp new file mode 100644 index 0000000..e23aa92 --- /dev/null +++ b/libstark/src/protocols/common/queries.hpp @@ -0,0 +1,20 @@ +#ifndef PROTOCOLS_COMMON_HPP__ +#define PROTOCOLS_COMMON_HPP__ + +#include "CryptoCommitment/MerkleCommitment.hpp" +#include + +namespace libstark{ +namespace Protocols{ + +// A set of indices of pairs of hashDigest_t blocks +// this is the minimal block that can be read and verified +typedef std::set rawQuery_t; + +// A serialize subset of a merkle tree as result +typedef std::vector rawResult_t; + +} //namespace Protocols +} //namespace libstark + +#endif //#ifndef PROTOCOLS_COMMON_HPP__ diff --git a/libstark/src/protocols/common/verifier.cpp b/libstark/src/protocols/common/verifier.cpp new file mode 100644 index 0000000..4783235 --- /dev/null +++ b/libstark/src/protocols/common/verifier.cpp @@ -0,0 +1,126 @@ +#include "verifier.hpp" +#include + +namespace libstark{ +namespace Protocols{ + +using Algebra::FieldElement; +using std::set; + +uniEvalView_t::uniEvalView_t(const short logSizeBytes_) : + view(logSizeBytes_),logSizeBytes(logSizeBytes_){}; +uniEvalView_t::uniEvalView_t() : view(10), logSizeBytes(1){}; + +void uniEvalView_t::fetchResults()const{ + for(const auto& q : queries){ + const size_t blockIdx = q.first; + const auto& blockData = view.readData(blockIdx); + const FieldElement* dataAsVec = (FieldElement*)(&blockData); + + for(const auto& qq : q.second){ + const short offsetInBlock = qq.first; + const FieldElement& res = dataAsVec[offsetInBlock]; + + //write value + for(auto& answer : qq.second){ + answer(res); + } + } + } +} + +rawQuery_t uniEvalView_t::getRawQuery()const{ + rawQuery_t res; + for(const auto& q : queries){ + const size_t blockIndex = q.first; + const size_t blockPairIndex = blockIndex>>1; + res.insert(blockPairIndex); + } + + return res; +} + +void uniEvalView_t::fillResultsAndCommitmentRandomly(){ + //fill commitment + { + //char* h_bytes = (char*)(&commitment); + //for(int i=0; i< sizeof(commitment); i++){ + // h_bytes[i] = rand(); + //} + } + + //fill results + rawResult_t results(expectedResultsLenght()); + + //for(auto& hash_val : results){ + // char* h_bytes = (char*)(&hash_val); + // for(int i=0; i< sizeof(hash_val); i++){ + // h_bytes[i] = rand(); + // } + //} + + digestResults(results); +} + +void uniEvalView_t::digestResults(const rawResult_t& results){ + //If there are no queries to this buffer it is skipped + if(queries.size() == 0){ + return; + } + + set queriesIndices; + for(const auto& p : queries){ + queriesIndices.insert(p.first); + } + + view.DeSerialize(queriesIndices , results); +} + +size_t uniEvalView_t::expectedResultsLenght()const{ + + //If there are no queries to this buffer it is skipped + if(queries.size() == 0){ + return 0; + } + + set queriesIndices; + for(const auto& p : queries){ + queriesIndices.insert(p.first); + } + + return view.getSerializationMapping(queriesIndices).size(); +} + +size_t uniEvalView_t::expectedQueriedFieldElementsNum()const{ + size_t res = 0; + for(const auto& blockQueries : queries){ + res+= blockQueries.second.size(); + } + return res; +} + +bool uniEvalView_t::verifyComitment()const{ + //If there are no queries to this buffer it is skipped + if(queries.size() == 0){ + return true; + } + + const bool res = (commitment == view.calculateRoot()); + if(!res){ + return false; + } + return true; +} + +void ResultLocation::addAnswerPtr(FieldElement* ptr){ + answerLocations_.push_back(ptr); +} + +void ResultLocation::answer(const FieldElement& res)const{ + for (const auto& e : answerLocations_){ + *e = res; + } +} + +} //namespace Protocols +} //namespace libstark diff --git a/libstark/src/protocols/common/verifier.hpp b/libstark/src/protocols/common/verifier.hpp new file mode 100644 index 0000000..6ac906a --- /dev/null +++ b/libstark/src/protocols/common/verifier.hpp @@ -0,0 +1,54 @@ +#ifndef PROTOCOLS_COMMON_VERIFIER_HPP__ +#define PROTOCOLS_COMMON_VERIFIER_HPP__ + +#include "protocols/protocol.hpp" +#include "protocols/common/queries.hpp" +#include "CryptoCommitment/MerkleCommitment.hpp" +#include +#include +#include +#include + +namespace libstark{ +namespace Protocols{ + +typedef std::function setVal_t; +// (blockIdx, offsetInBlock) -> locations to keep results +typedef std::map > > expliciteQueries_t; + +class uniEvalView_t{ +public: + + uniEvalView_t(const short logSizeBytes_); + uniEvalView_t(); + CryptoCommitment::SparceMerkleTree view; + CryptoCommitment::hashDigest_t commitment; + + expliciteQueries_t queries; + + void fetchResults()const; + rawQuery_t getRawQuery()const; + void fillResultsAndCommitmentRandomly(); + void digestResults(const rawResult_t& results); + bool verifyComitment()const; + size_t expectedResultsLenght()const; + size_t expectedQueriedFieldElementsNum()const; + short getLogSizeBytes()const{return logSizeBytes;} + +private: + short logSizeBytes; +}; + +class ResultLocation{ +public: + void addAnswerPtr(Algebra::FieldElement* ptr); + void answer(const Algebra::FieldElement& res)const; + +private: + std::vector answerLocations_; +}; + +} //namespace Protocols +} //namespace libstark + +#endif //#ifndef PROTOCOLS_COMMON_VERIFIER_HPP__ diff --git a/libstark/src/protocols/protocol.cpp b/libstark/src/protocols/protocol.cpp new file mode 100644 index 0000000..30487dc --- /dev/null +++ b/libstark/src/protocols/protocol.cpp @@ -0,0 +1,440 @@ +#include "protocol.hpp" +#include "common/Utils/Timing.hpp" +#include "common/Utils/specsPrint.hpp" +#include "common/Utils/TaskReporting.hpp" +#include "languages/Bair/BairWitnessChecker.hpp" +#include "languages/Acsp/AcspWitnessChecker.hpp" +#include "reductions/BairToAcsp/BairToAcsp.hpp" +#include "protocols/Ali/verifier.hpp" +#include "protocols/Ali/prover.hpp" + +#include +#include + +namespace libstark{ +namespace Protocols{ + +//the following are UBUNTU/LINUX ONLY terminal color codes. +#define RESET "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ +#define BLUE "\033[34m" /* Blue */ +#define MAGENTA "\033[35m" /* Magenta */ +#define CYAN "\033[36m" /* Cyan */ +#define WHITE "\033[37m" /* White */ +#define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ +#define BOLDRED "\033[1m\033[31m" /* Bold Red */ +#define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ +#define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ +#define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ +#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ +#define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ +#define BOLDWHITE "\033[1m\033[37m" /* Bold White */ + +namespace{ +const auto VERIFIER_COLOR = YELLOW; + +void startColor(const char* color){ + //std::cout< 1024LL){ + currSize /= 1024LL; + i++; + } + + return std::to_string(currSize) + " " + suffix[i]; +} + +std::string secondsToString(double seconds){ + if(seconds >= 60LL*60LL*24LL){ + return std::to_string(seconds/(60LL*60LL*24LL)) + " Days"; + } + + if(seconds >= 60LL*60LL){ + return std::to_string(seconds/(60LL*60LL)) + " Hours"; + } + + if(seconds >= 60LL){ + return std::to_string(seconds/(60LL)) + " Minutes"; + } + + return std::to_string(seconds) + " Seconds"; +} + +void printSpecs(const double proverTime, const double verifierTime, const size_t proofGeneratedBytes, const size_t proofSentBytes, const size_t queriedDataBytes){ + startSpecs(); + specsPrinter specs("Protocol execution measurements"); + specs.addLine("Prover time",secondsToString(proverTime)); + specs.addLine("Verifier time",secondsToString(verifierTime)); + specs.addLine("Total proof oracles size",numBytesToString(proofGeneratedBytes)); + specs.addLine("Total communication complexity",numBytesToString(proofSentBytes)); + specs.addLine("Query complexity",numBytesToString(queriedDataBytes)); + specs.print(); + + resetColor(); +} + +void printSpecsCSV(const double proverTime, const double verifierTime, const size_t proofGeneratedBytes, const size_t proofSentBytes, const size_t queriedDataBytes){ + return; + startSpecs(); + std::cout<<"Comma Seperated Valued (CSV) specifications:"< > univariatePolys; + { + const vector inputDegrees(totalVars * 2, PolynomialDegree(1)); + for (const auto& c : instance.constraintsAssignment().constraints()){ + size_t numVarsUsed = 0; + size_t lastVarUsed = 0; + for (size_t i = 0; i< totalVars * 2; i++){ + if (c->isEffectiveInput(i)){ + numVarsUsed++; + lastVarUsed = i; + } + } + if ((numVarsUsed == 1) && (lastVarUsed < totalVars)){ + univariateConstraintsTime++; + const auto degreeBound = c->getDegreeBound(inputDegrees); + const size_t interpolationBasisSize = ceil(Log2(PolynomialDegree::integral_t(degreeBound) + 1)); + const auto interpolationBasis = getStandartBasis(interpolationBasisSize); + const vector orderedBasis(interpolationBasis.begin(), interpolationBasis.end()); + + //construct the evaluation + vector evaluation(POW2(interpolationBasisSize)); + { + vector assignment(totalVars * 2); + for (size_t i = 0; i< evaluation.size(); i++){ + assignment[lastVarUsed] = getSpaceElementByIndex(orderedBasis, zero(), i); + evaluation[i] = c->eval(assignment); + } + } + if ((numVarsUsed == 1) && (lastVarUsed < totalVars)){ + univariateConstraintsTime++; + const auto degreeBound = c->getDegreeBound(inputDegrees); + const size_t interpolationBasisSize = ceil(Log2(PolynomialDegree::integral_t(degreeBound) + 1)); + const auto interpolationBasis = getStandartBasis(interpolationBasisSize); + const vector orderedBasis(interpolationBasis.begin(), interpolationBasis.end()); + + //construct the evaluation + vector evaluation(POW2(interpolationBasisSize)); + { + vector assignment(totalVars * 2); + for (size_t i = 0; i< evaluation.size(); i++){ + assignment[lastVarUsed] = getSpaceElementByIndex(orderedBasis, zero(), i); + evaluation[i] = c->eval(assignment); + } + const UnivariatePolynomialGeneral poly(evaluation, orderedBasis, zero()); + //add poly to the menaged set + { + bool found = false; + for (auto& p : univariatePolys){ + if (p.first == poly){ + p.second++; + found = true; + break; + } + } + if (!found){ + univariatePolys.push_back(pair(poly, 1)); + } + } + } + } + } + } + + size_t usedVarsAmount = 0; + for (size_t i = 0; i < totalVars; i++){ + if (instance.constraintsAssignment().varUsed(i)){ + usedVarsAmount++; + } + else if (instance.constraintsPermutation().varUsed(i)){ + usedVarsAmount++; + } + else if (instance.constraintsAssignment().varUsed(i + totalVars)){ + usedVarsAmount++; + } + else if (instance.constraintsPermutation().varUsed(i + totalVars)){ + usedVarsAmount++; + } + } + + const size_t unroutedAmount = totalVars - amountRouted; + + specs.addLine("number of variables used by constraint systems",to_string(usedVarsAmount)); + specs.addLine("number of variables routed",to_string(amountRouted)); + specs.addLine("number of variables not routed",to_string(unroutedAmount)); + } + + specs.print(); +} + + void printAcspInstanceSpec(const AcspInstance& instance){ + specsPrinter specs("ACSP Specifications"); + specs.addLine("field size","2^64"); + specs.addLine("number of algebraic-registers (|\\Tau|)",to_string(instance.witnessDegreeBound().size())); + + size_t numNeighbors = 0; + for(const auto& vn : instance.neighborPolys()){ + numNeighbors += vn.size(); + } + + specs.addLine("number of neighbors (|N|)",to_string(numNeighbors)); + specs.addLine("vanishing space size","2^"+to_string((int)Log2(instance.vanishingSet().size()))); + + vector inputDegrees(1,PolynomialDegree(1)); + { + for(size_t wIndex = 0; wIndex < instance.neighborPolys().size(); wIndex++){ + const PolynomialDegree witnessDeg = instance.witnessDegreeBound()[wIndex]; + for (const auto& n : instance.neighborPolys()[wIndex]){ + inputDegrees.push_back(n->getDegreeBound(witnessDeg)); + } + } + } + specs.addLine("composition degree bound",to_string(PolynomialDegree::integral_t(instance.constraintPoly().getDegreeBound(inputDegrees)))); + + specs.print(); + } + + void printAprInstanceSpec(const AcspInstance& instance){ + using namespace Ali::details::PCP_common; + specsPrinter specs("APR Specifications"); + specs.addLine("field size","2^64"); + specs.addLine("number of algebraic-registers (|\\Tau|)",to_string(instance.witnessDegreeBound().size())); + + size_t numNeighbors = 0; + for(const auto& vn : instance.neighborPolys()){ + numNeighbors += vn.size(); + } + + specs.addLine("number of neighbors (|N|)",to_string(numNeighbors)); + specs.addLine("witness (f) evaluation space size (|L|)","2^" + to_string(basisForWitness(instance).basis.size())); + specs.addLine("constraint (g) evaluation space size (|L_{cmp}|)","2^" + to_string(basisForConsistency(instance).basis.size())); + const size_t maxDeg = ceil(Log2(PolynomialDegree::integral_t(maximalPolyDegSupported_Witness(instance)))); + specs.addLine("witness (f) maximal rate (\\rho_{max})","2^{-" + to_string(basisForWitness(instance).basis.size() - maxDeg)+"}"); + const int compDeg = ceil(Log2(PolynomialDegree::integral_t(composition_div_ZH_degreeBound(instance)))); + specs.addLine("constraint (g) rate (\\rho_{cmp})","2^{-" + to_string(basisForConsistency(instance).basis.size() - compDeg)+"}"); + + { + using namespace Ali::details::SoundnessParameters; + specs.addLine("zero knowledge parameter (k)","1"); + specs.addLine("rate parameter (R)",to_string(Eta)); + specs.addLine("constraints degree log (d)",to_string(compDeg)); + } + + specs.print(); + } + + +} + +bool executeProtocol(const BairInstance& instance, const BairWitness& witness, bool testBair, bool testAcsp, bool testPCP){ + prn::printBairInstanceSpec(instance); + unique_ptr acspInstance = CBairToAcsp::reduceInstance(instance); + prn::printAcspInstanceSpec(*acspInstance); + prn::printAprInstanceSpec(*acspInstance); + + if(testBair){ + if(!BairWitnessChecker::verify(instance, witness)){ + return false; + } + } + + + std::cout<<"Constructing APR (ACSP) witness:"; + bool doStatusLoop = true; + Timer reductionTimer; + std::thread barManager( + [&](){ + unsigned int sleepTime = 100; + while(doStatusLoop){ + std::cout<<"."< acspWitness = CBairToAcsp::reduceWitness(instance, witness); + doStatusLoop = false; + barManager.join(); + std::cout<<"("< acspInstance = CBairToAcsp::reduceInstance(instance); + prn::printAcspInstanceSpec(*acspInstance); + + using namespace Ali::Verifier; + using namespace Ali::Prover; + + const auto RS_verifier = Biased_verifier; + const auto RS_prover = Biased_prover; + + verifier_t verifier(*acspInstance, RS_verifier); + prover_t* prover_dummy = nullptr; + Protocols::executeProtocol(*prover_dummy,verifier,true); +} + +} //namespace Protocols +} //namespace libstark diff --git a/libstark/src/protocols/protocol.hpp b/libstark/src/protocols/protocol.hpp new file mode 100644 index 0000000..4d2dee7 --- /dev/null +++ b/libstark/src/protocols/protocol.hpp @@ -0,0 +1,92 @@ +#ifndef PROTOCOL_HPP__ +#define PROTOCOL_HPP__ + +#include "languages/Bair/BairInstance.hpp" +#include "languages/Bair/BairWitness.hpp" +#include "languages/Acsp/AcspInstance.hpp" +#include "languages/Acsp/AcspWitness.hpp" +#include +#include +#include +#include + +namespace libstark{ +namespace Protocols{ + +// +// There are many different ways to define a generic protocol. +// +// + +// +// Transcript data related types +// +class TranscriptMessage{ +public: +virtual ~TranscriptMessage(){}; +}; + +typedef std::unique_ptr msg_ptr_t; + +// +// Protocol parties definitions +// +class PartieInterface{ +public: + //Receive a message and return True if protocol finished + virtual void receiveMessage(const TranscriptMessage& msg) = 0; + + //Send a message based on current internal state + virtual msg_ptr_t sendMessage() = 0; + + virtual ~PartieInterface(){}; +}; + +class verifierInterface : public PartieInterface{ +public: + virtual ~verifierInterface(){}; + virtual bool doneInteracting()const=0; + virtual bool verify()const=0; + + virtual size_t expectedCommitedProofBytes()const = 0; + virtual size_t expectedSentProofBytes()const = 0; + virtual size_t expectedQueriedDataBytes()const = 0; + + virtual void fillResultsAndCommitmentRandomly()=0; +}; + + +// +// A partie that wants to verify a very big an fixed input +// is closed to some code C. +// +// The verifier first interacts with the prover, +// and after that reads from the input. +// +typedef std::map> queriesToInp_t; +class IOPP_verifierInterface : public verifierInterface{ +public: + virtual ~IOPP_verifierInterface(){}; + virtual const queriesToInp_t& queriesToInput()const=0; +}; + +// +// Protocol execution algorithm +// +bool executeProtocol(PartieInterface& prover, verifierInterface& verifier, const bool onlyVerifierData = false); + +bool executeProtocol(const BairInstance& instance, const BairWitness& witness, bool testBair = false, bool testAcsp = false, bool testPCP = false); +void simulateProtocol(const BairInstance& instance); + +//printouts +namespace prn{ + void printBairInstanceSpec(const BairInstance& instance); + void printAcspInstanceSpec(const AcspInstance& instance); + void printAcspWitnessSpec(const AcspWitness& witness); + void printAcspPairSpec(const AcspInstance& instance, const AcspWitness& witness); +} + +} //namespace Protocols +} //namespace libstark + +#endif //ifndef PROTOCOL_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/BairToAcsp.cpp b/libstark/src/reductions/BairToAcsp/BairToAcsp.cpp new file mode 100644 index 0000000..afcba0a --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/BairToAcsp.cpp @@ -0,0 +1,334 @@ +/**************************************** BairToAcsp.cpp *****************************************/ +/** + * @file. + * + * Implements the BairToAcsp reduction class. + */ + /***********************************************************************************************/ + +#include "BairToAcsp.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include "common/Algebra/LinearSpace.hpp" +#include "common/Utils/TaskReporting.hpp" +#include "Details/witnessReduction.hpp" +#include "Details/constraints.hpp" +#include "Details/common.hpp" +#include "Details/instanceMappings.hpp" +#include "Details/neighborsConstructor.hpp" +#include "Details/spaces.hpp" +#include "Details/boundaryConstraints.hpp" +#include "Details/AcspSummandsPolynomial.hpp" + +#include +#include + +#include +#include + +namespace libstark { + +using Algebra::FieldElement; +using Algebra::LinearSpace; +using Algebra::UnivariatePolynomialInterface; +using libstark::BairToAcsp::instanceMappings; +using libstark::BairToAcsp::common; +using libstark::BairToAcsp::AcspNeighbors; +using libstark::BairToAcsp::CS_testLocations; +using libstark::BairToAcsp::spaces; +using libstark::BairToAcsp::constraints; +using libstark::BairToAcsp::reduceBoundaryConstraints; +using Infrastructure::POW2; + +using std::unique_ptr; +using std::move; +using std::array; +using std::vector; +using std::pair; + +unique_ptr CBairToAcsp::reduceWitness( const BairInstance& instance, const BairWitness& witness){ + + return BairToAcsp::witnessReduction::reduceWitness(instance,witness); +} + + +/********************************************** + * Instance Reduction + *********************************************/ + +namespace{ + //get vanishing space properties + vector getVanishingSpace(const spaces& spacesGenerator){ + + const auto basis = spacesGenerator.getVanishingSpaceBasis(); + const vector result(basis.begin(),basis.end()); + + //return the result + return result; + } + + +vector compositionAlg(const AcspInstance& instance, const AcspWitness& witness, const vector& basisPCPP, const FieldElement& shiftPCPP, const bool witnessIsEvaluation){ + using std::vector; + using std::min; + using std::max; + using std::unique_ptr; + using Algebra::FieldElement; + using Algebra::PolynomialDegree; + using Algebra::zero; + using Algebra::UnivariatePolynomialGeneral; + using Algebra::mapIntegerToFieldElement; + using Algebra::novelFFT; + using Infrastructure::Log2; + using BairToAcsp::AcspSummandsPolynomial; + + //the constraints poly + const AcspSummandsPolynomial& constraintsPoly = *(dynamic_cast(&instance.constraintPoly())); + + // + //Get the composition degree bound + // + vector constraintsInputDegrees; + + // first input is "x" which has degree 1 + constraintsInputDegrees.push_back(PolynomialDegree(1)); + + // rest are composition of neighbor with witness + for(size_t wIndex = 0 ; wIndex < witness.assignmentPolys().size(); wIndex++){ + const auto witnessDegree = witness.assignmentPolys()[wIndex]->getDegree(); + for (const auto& n : instance.neighborPolys()[wIndex]){ + constraintsInputDegrees.push_back(n->getDegreeBound(witnessDegree)); + } + } + + // get the composition degree bound + const PolynomialDegree degBound = constraintsPoly.getDegreeBound_DividedByZH(constraintsInputDegrees); + const size_t degBoundLog = ceil(Log2(max(PolynomialDegree::integral_t(degBound)+1,PolynomialDegree::integral_t(2)))); + + // + //construct composition evaluation basis + // + vector compositionBasis(degBoundLog); + if(witnessIsEvaluation){ + compositionBasis = vector(basisPCPP.begin(),basisPCPP.begin()+degBoundLog); + } + else{ + for(size_t i=0; i< degBoundLog; i++){ + compositionBasis[i] = mapIntegerToFieldElement(i,1,1); + _COMMON_ASSERT(compositionBasis[i] == basisPCPP[i], "Unsupported basis"); + } + } + + const FieldElement evaluationAffineShift = (witnessIsEvaluation? shiftPCPP : mapIntegerToFieldElement(degBoundLog+1,1,1)); + + vector evaluation(Infrastructure::POW2(compositionBasis.size())); + + vector neighborsIndexOffset; + size_t numNeighbors = 0; + for(size_t wIndex = 0 ; wIndex < witness.assignmentPolys().size(); wIndex++){ + neighborsIndexOffset.push_back(numNeighbors); + for(size_t n=0; n < instance.neighborPolys()[wIndex].size(); n++){ + numNeighbors++; + } + } + + const size_t wDegBound = PolynomialDegree::integral_t(instance.witnessDegreeBound()[0])+1; + for(const auto& d: instance.witnessDegreeBound()){ + _COMMON_ASSERT((long long)wDegBound-1 == PolynomialDegree::integral_t(d), "Degrees of witness columns are expected to be equal"); + } + const size_t wDim = Log2(wDegBound); + const vector wEvalBasis(compositionBasis.begin(), compositionBasis.begin()+wDim); + const vector wOffsetBasis(compositionBasis.begin()+wDim, compositionBasis.end()); + const size_t numEvalOffsets = evaluation.size() >> wDim; + const size_t numWitnessOffsets = Infrastructure::POW2(wOffsetBasis.size()); + + vector> witnessOffsets; + { + witnessOffsets.resize(numWitnessOffsets); + const FieldElement carryOut = mapIntegerToFieldElement(wDim,1,1); + for(size_t i=0; i fftInstance; + if(!witnessIsEvaluation){ + typedef vector polyCoeffs_t; + vector polys(numWitnesses); + for(unsigned int wIndex = 0 ; wIndex < numWitnesses; wIndex++){ + polys[wIndex] = witness.assignmentPolys()[wIndex]->getCoefficients(); + } + + fftInstance = std::unique_ptr(new novelFFT(wEvalBasis,std::move(polys),numWitnesses,Algebra::zero())); + } + + //construct evaluation + { + TASK("Evaluate the composition polynomial divided by Z_H over a space of degree " + std::to_string(compositionBasis.size())); + + vector witnessEval; + if(!witnessIsEvaluation){ + witnessEval.resize(witnessEvalBlockSize); + } + + for(unsigned long long currOffset = 0; currOffset < numEvalOffsets; currOffset++){ + TASK("Evaluate 1 offset of total " + std::to_string(numEvalOffsets)); + + if(!witnessIsEvaluation){ + //Evaluate witnesses + fftInstance->FFT(witnessOffsets[currOffset],&witnessEval[0],numWitnesses*cosetSize); + } + +#pragma omp parallel for schedule(guided) + for(unsigned long long i=0; i< wDegBound; i++){ + + const long currLocation = i + (currOffset<> assignment(1); + assignment[0].resize(1+numNeighbors); + assignment[0][0] = x; + + for(size_t wIndex = 0 ; wIndex < witness.assignmentPolys().size(); wIndex++){ + for(size_t n=0; n < instance.neighborPolys()[wIndex].size(); n++){ + + const FieldElement neighborRes = instance.neighborPolys()[wIndex][n]->eval(x); + FieldElement witnessRes; + + if(!witnessIsEvaluation){ + const size_t nInCosetIdx = mapFieldElementToInteger(0,wDim,neighborRes); + size_t nOffsetIdx = mapFieldElementToInteger(degBoundLog+2,1,neighborRes) + 2*mapFieldElementToInteger(wDim,1,neighborRes); + if (nOffsetIdx == 2){nOffsetIdx = 0;} + if (nOffsetIdx == 3){nOffsetIdx = 2;} + const size_t nIdx = nInCosetIdx + (nOffsetIdx<eval(neighborRes); + } + + assignment[0][1 + neighborsIndexOffset[wIndex] + n] = witnessRes; + } + } + //evaluate and return + evaluation[currLocation] = constraintsPoly.evalDividedByZH(assignment)[0]; + } + } + } + + { + TASK("Doing LDE"); + const novelFFT fftCol(compositionBasis, std::move(evaluation)); + vector cosetShiftsBasis(basisPCPP.begin()+degBoundLog, basisPCPP.end()); + vector cosetShifts(POW2(cosetShiftsBasis.size())); + for(size_t i=0; i< cosetShifts.size(); i++){ + cosetShifts[i] = getSpaceElementByIndex(cosetShiftsBasis,shiftPCPP - evaluationAffineShift, i); + } + + vector res(POW2(basisPCPP.size())); + fftCol.FFT(cosetShifts,&res[0],POW2(compositionBasis.size())); + return res; + } +} + +class evaluatedUniPoly : public Algebra::UnivariatePolynomialGeneral{ + public: + evaluatedUniPoly(const AcspInstance& instance, const UnivariatePolynomialInterface& witness): + Algebra::UnivariatePolynomialGeneral(witness){ + using std::unique_ptr; + using std::max; + using Algebra::zero; + using Algebra::PolynomialDegree; + using Infrastructure::Log2; + + // + //define interpolation space + // + const size_t vanishingSpaceDim = Log2(instance.vanishingSet().size()); + const size_t witnessDegLog = ceil(Log2(PolynomialDegree::integral_t(witness.getDegree()))); + const size_t witnessEvalBasisSize = max(vanishingSpaceDim + 1, witnessDegLog); + evaluationBasis_ = Algebra::getStandartOrderedBasis(witnessEvalBasisSize); + evaluationOffset_ = zero(); + + // + //Evaluate witness on relevant spaces for faster composition + // + { + TASK("Evaluate the witness polynomial over the closure of the vanishing set to the neighbors"); + witnessEval_ = witness.eval(evaluationBasis_,evaluationOffset_); + } + + } + + Algebra::FieldElement eval(const FieldElement& x)const{ + const size_t x_indx = Algebra::mapFieldElementToInteger(0,64,x-evaluationOffset_); + if(x_indx < Infrastructure::POW2(evaluationBasis_.size())){ + return witnessEval_[x_indx]; + } + return UnivariatePolynomialGeneral::eval(x); + } + + private: + FieldElement evaluationOffset_; + vector evaluationBasis_; + vector witnessEval_; +}; + +vector> witnessCheckerHelper(const AcspInstance& instance, const AcspWitness& witness){ + vector> res; + for(size_t wIndex=0; wIndex < witness.assignmentPolys().size(); wIndex++){ + res.push_back(unique_ptr(new evaluatedUniPoly(instance,*witness.assignmentPolys()[wIndex]))); + } + + return res; +} + + +} +/* + * Construct an Acsp partial instance from a Bair full instance (top-level arithmetization function for partial instances) + */ +unique_ptr CBairToAcsp::reduceInstance(const BairInstance& instance){ + // get common deffintion + const common commonDef(instance); + + // fetch more parameters + const instanceMappings instanceMapping(commonDef); + const spaces spacesGenerator(commonDef); + const CS_testLocations testLocations(commonDef); + const AcspNeighbors neighborsWithInterpretation(instance, commonDef, instanceMapping,testLocations); + + // construct the vanishing set + unique_ptr vanishingSet(new LinearSpace(getVanishingSpace(spacesGenerator))); + + // construct the neighbors vector + vector neighbors = neighborsWithInterpretation.getNeighborPolynomials(); + + //construct the constraints polynomial + unique_ptr constraintsPoly(constraints::getConstraintsPoly(instance,neighborsWithInterpretation,commonDef,instanceMapping, spacesGenerator, testLocations)); + + //construct the boundary constraints + std::vector boundaryConstraints = reduceBoundaryConstraints(instance.boundaryConstraints(),commonDef); + + //construct and return + unique_ptr res(new AcspInstance(move(vanishingSet), move(neighbors), move(constraintsPoly), commonDef.witnessDegreeBound(), boundaryConstraints, compositionAlg, witnessCheckerHelper)); + + return res; +} + +} // namespace libstark + diff --git a/libstark/src/reductions/BairToAcsp/BairToAcsp.hpp b/libstark/src/reductions/BairToAcsp/BairToAcsp.hpp new file mode 100644 index 0000000..fcd8bbc --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/BairToAcsp.hpp @@ -0,0 +1,46 @@ +/************************************** TinyRAMtoBair.hpp ****************************************/ + +/** + * @file BEXtoAcsp.hpp + * + * Declares the BairToAcsp reduction class + */ + + /***********************************************************************************************/ + +#pragma once +#ifndef __Bair_TO_Acsp_HPP +#define __Bair_TO_Acsp_HPP + +#include "languages/Bair/BairInstance.hpp" +#include "languages/Bair/BairWitness.hpp" +#include "languages/Acsp/AcspInstance.hpp" +#include "languages/Acsp/AcspWitness.hpp" + +#include + +namespace libstark{ + + +class CBairToAcsp{ +public: + /** + * Reduces an BairInstance to an AcspInstance. + * @param[in] partialInstance + * @return a pointer to an AcspInstance. + */ + static ::std::unique_ptr reduceInstance(const BairInstance& instance); + + /** + * Reduces a BairExecutedWitness to an AcspWitness. + * @param[in] fullInstance + * @param[in] witness + * @return a pointer to an AcspWitness. + */ + static ::std::unique_ptr reduceWitness(const BairInstance& instance, const BairWitness& witness); + +}; // class BairToAcsp + +}//namespace libstark + +#endif // __Bair_TO_Acsp_HPP diff --git a/libstark/src/reductions/BairToAcsp/Details/AcspRationalSystem.hpp b/libstark/src/reductions/BairToAcsp/Details/AcspRationalSystem.hpp new file mode 100644 index 0000000..6cfb2bd --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/AcspRationalSystem.hpp @@ -0,0 +1,25 @@ +#include "algebraLib/SubspacePolynomial.hpp" +#include "common/Algebra/ShiftedSubspacePolynomial.hpp" + +#ifndef ACSP_RAT_SYS +#define ACSP_RAT_SYS + +namespace libstark{ +namespace BairToAcsp{ + +class AcspRationalSystem : public RationalConstraintSys{ +public: + virtual ~AcspRationalSystem(){}; +private: + typedef size_t polyId_t; + typedef std::vector translationTable_t; + + + const std::unique_ptr csTime_; + const std::unique_ptr csMem_; +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //ifndef ACSP_RAT_SYS diff --git a/libstark/src/reductions/BairToAcsp/Details/AcspSummandsPolynomial.cpp b/libstark/src/reductions/BairToAcsp/Details/AcspSummandsPolynomial.cpp new file mode 100644 index 0000000..eade3fa --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/AcspSummandsPolynomial.cpp @@ -0,0 +1,325 @@ +/** AcspSummandsPolynomial.cpp **/ +/* This class is designed to hold a multivariate-polynomial precisely of the form that arises in the BS/BGHSP PCP. +For more details see hpp file +*/ + +#include "AcspSummandsPolynomial.hpp" +#include "common/Infrastructure/Infrastructure.hpp" +#include "common/Utils/ErrorHandling.hpp" +#include + +using Algebra::FieldElement; +using Algebra::SubspacePolynomial; +using Algebra::elementsSet_t; +using Algebra::PolynomialInterface; +using Algebra::PolynomialDegree; + +using std::vector; +using std::unique_ptr; +using std::max; +using std::string; +using std::exception; + +namespace{ +class not_a_polynomial_exeption : public exception{}; +} + +namespace libstark{ +namespace BairToAcsp{ + +typedef AcspSummandsPolynomial::polyID polyID; + + /// Creates a new AcspSummandsPolynomial, and sets \f$H\f$ to be \f$ span( spanSetH ) \f$ + AcspSummandsPolynomial::AcspSummandsPolynomial(const Algebra::elementsSet_t& spanSetH): Z_H(spanSetH) {}; + + //Copy constructor + AcspSummandsPolynomial::AcspSummandsPolynomial(const AcspSummandsPolynomial& that):Z_H(that.Z_H),subspacePolys(that.subspacePolys),denomsVec_(that.denomsVec_),summands(that.summands){ + + + } + + + /// adds a subspace polynomial vanishing on T into subspacePolys and returns its id, i.e, its index in subspacePolys + polyID AcspSummandsPolynomial::addSubspacePoly(const Algebra::elementsSet_t& T){ + ///verify \f$ span(T) \subset H \f$ by checking \f$ \forall t \in T : Z_H(t) = 0 \f$ + { + const Algebra::FieldElement ZERO = Algebra::zero(); + for(const auto& t : T){ + if (Z_H.eval(t) != ZERO) _COMMON_FATAL("T must be a subset of H"); + } + } + + subspacePolys.push_back(SubspacePolynomial(T)); + return subspacePolys.size() -1; + + } + + /// evaluates this polynomial on \f$ a \f$ + FieldElement AcspSummandsPolynomial::eval(const EvaluationPoint& a)const { + return evalOnSet({a})[0]; + } + + size_t AcspSummandsPolynomial::numVars()const { + _COMMON_FATAL("Not implemented"); + } + + vector AcspSummandsPolynomial::evalOnSet(const vector>& x_set)const{ + + try{ + vector res = evalDividedByZH(x_set,true); + for(size_t i=0; i AcspSummandsPolynomial::evalDividedByZH(const vector& a_set,const bool allowEvalIn_H)const{ + + //constants + static const FieldElement ZERO = Algebra::zero(); + static const FieldElement ONE = Algebra::one(); + + //it pays to store in advance the values of the different subspace polys on a + //as each such value appears in many summands + vector> subspaceValues(a_set.size()); + + for (size_t j =0; j< a_set.size(); j++){ + subspaceValues[j].resize(subspacePolys.size()); + for (polyID i = 0; i < subspacePolys.size(); i++){ + subspaceValues[j][i] = subspacePolys[i].eval(a_set[j][0]); + } + } + + //it pays of to do all the needed inversions together + vector valsToInverse(denomsVec_.size()*a_set.size()); + + for (size_t j =0; j< a_set.size(); j++){ + for (polyID i=0; i invRes = Algebra::invertPointwise(valsToInverse); + vector> denomsVals(a_set.size()); + + for (size_t j =0; j< a_set.size(); j++){ + denomsVals[j].resize(denomsVec_.size()); + for (polyID i=0; i res(a_set.size(),ZERO); + + for (unsigned int i = 0;i< summands.size(); i++){ + for( size_t j=0; j< a_set.size(); j++){ + res[j] += summands[i].eval(a_set[j], subspaceValues[j], denomsVals[j]); + } + } + + return res; + } + + + + /// Next two methods commented out, see explanation above + /// construct the summand 1/(Z_S+c) * P_i, where c=Z_S(shift) + /// It is enforced that \f$ shift \in H \f$ (otherwise _COMMON_FATAL is called) + void AcspSummandsPolynomial::addSummand(const polyID Z_S, const FieldElement& shift, const Constraint& P_i){ + ///verify \f$ shift \in H \f$ by checking \f$ Z_H(shift) = 0 \f$ + { + const Algebra::FieldElement ZERO = Algebra::zero(); + if (Z_H.eval(shift) != ZERO) _COMMON_FATAL("shift must be an element of H"); + } + const FieldElement c = subspacePolys[Z_S].eval(shift); + summands.push_back(AcspConstraintSummand(denomsVec_.size(), P_i)); + denomsVec_.push_back(DenominatorType(Z_S,c)); + } + + /// construct the summand 1/(Z_S+c) * P_i * W, where c=Z_S(shift) and W is the product of polynomials in auxPolys + /// It is enforced that \f$ shift \in H \f$ and all the shifts are in \f$ H \f$ as well (otherwise _COMMON_FATAL is called) + void AcspSummandsPolynomial::addSummand(const polyID Z_S, const FieldElement& shift, const Constraint& P_i, const AuxPolyVec& auxVec){ + const FieldElement ZERO = Algebra::zero(); + + ///verify \f$ shift \in H \f$ by checking \f$ Z_H(shift) = 0 \f$ + { + if (Z_H.eval(shift) != ZERO) _COMMON_FATAL("shift must be an element of H"); + } + + vector shifts; + vector auxPolys; + ///verify all shifts are in \f$ H \f$, and, on the way, insert their images into the shifts vector + { + + for(const auto& s : auxVec){ + if (Z_H.eval(s.second) != ZERO) _COMMON_FATAL("all shifts must be in H"); + const FieldElement polyShift = subspacePolys[s.first].eval(s.second); + shifts.push_back(polyShift); + + auxPolys.push_back(s.first); + } + } + const FieldElement c = subspacePolys[Z_S].eval(shift); + summands.push_back(AcspConstraintSummand(denomsVec_.size(), P_i, auxPolys,shifts)); + denomsVec_.push_back(DenominatorType(Z_S,c)); + } + + /** + * @brief return a clone of the current polynomial + * @return a unique_ptr of PolynomialInterface, + * representing a polynomial equivalent to current + */ + unique_ptr AcspSummandsPolynomial::clone()const{ + return unique_ptr(new AcspSummandsPolynomial(*this)); + } + + /** + * @brief Returns an upper bound for degree of this polynomial + * when composed with univariate polynomials (as its variables) + * @param degrees vector of univariate polynomial, name it \f$\vec{d}\f$ + * @return a degree \f$D\f$ such that + * \f$ D \ge \deg P\circ\vec{Q} \f$ when \f$P\f$ is the current polynomial, + * each \f$Q_i\f$ is a univariate polynomial of degree \f$d_i\f$ at most, + * and \f$(P\circ\vec{Q})(x) = P(Q_1(x),Q(2),\dots,Q_n(x))\f$ + */ + PolynomialDegree AcspSummandsPolynomial::getDegreeBound(const vector& inputDegreesBound)const{ + PolynomialDegree maxSummandDeg(-1); + + for(const auto& summand : summands){ + const PolynomialDegree currDeg = summand.getDegreeBound(inputDegreesBound); + maxSummandDeg = max(currDeg,maxSummandDeg); + } + + return degreeOfProduct(Z_H.getDegree(),maxSummandDeg); + } + + /** + * @brief Returns an upper bound for degree of this polynomial divided by Z_H + * when composed with univariate polynomials (as its variables) + * @param degrees vector of univariate polynomial, name it \f$\vec{d}\f$ + * @return a degree \f$D\f$ such that + * \f$ D \ge \deg P\circ\vec{Q} \f$ when \f$P\f$ is the current polynomial, + * each \f$Q_i\f$ is a univariate polynomial of degree \f$d_i\f$ at most, + * and \f$(P\circ\vec{Q})(x) = P(Q_1(x),Q(2),\dots,Q_n(x))\f$ + */ + PolynomialDegree AcspSummandsPolynomial::getDegreeBound_DividedByZH(const vector& inputDegreesBound)const{ + PolynomialDegree maxSummandDeg(-1); + + for(const auto& summand : summands){ + const PolynomialDegree currDeg = summand.getDegreeBound(inputDegreesBound); + maxSummandDeg = max(currDeg,maxSummandDeg); + } + + return maxSummandDeg; + } + + + + /***AcspConstraintSummand class methods ****/ + + //move constructor + AcspSummandsPolynomial::AcspConstraintSummand::AcspConstraintSummand(AcspConstraintSummand&& rhs) : + denominatorIndex_(std::move(rhs.denominatorIndex_)), + P_i(std::move(rhs.P_i)), + auxPolys(std::move(rhs.auxPolys)), + shifts(std::move(rhs.shifts)){ + } + + + //construct the summand 1/(Z_S+c) * P_i. Note that P_i is without const - cause it's a unique_ptr, so it needs to be moved and destroyed. + AcspSummandsPolynomial::AcspConstraintSummand::AcspConstraintSummand(const polyID denomIndex , const Constraint& P_i): + denominatorIndex_(denomIndex),P_i(P_i.clone()){}; + + //construct the summand 1/(Z_S+c) * P_i * W, where W is the product of polynomials in auxPolys + AcspSummandsPolynomial::AcspConstraintSummand::AcspConstraintSummand(const polyID denomIndex, const Constraint& P_i, const vector& auxPolys, const vector& shifts): + denominatorIndex_(denomIndex), + P_i(P_i.clone()), + auxPolys(auxPolys), + shifts(shifts){}; + + //copy constructor + AcspSummandsPolynomial::AcspConstraintSummand::AcspConstraintSummand(const AcspConstraintSummand& src): + denominatorIndex_(src.denominatorIndex_), + P_i(src.P_i->clone()), + auxPolys(src.auxPolys), + shifts(src.shifts){}; + + + /// evaluates the summand on a given point a - important point evaluates without the \f$ Z_H \f$ part! + /// it receives as input the array subspaceValues containing the evaluations on a of the subspace polys + /// associated with the AcspConstraintPolynomial object + + FieldElement AcspSummandsPolynomial::AcspConstraintSummand::eval(const EvaluationPoint& a, const vector& subspaceValues, const vector& denomsValues) const{ + static const FieldElement ZERO = Algebra::zero(); + + FieldElement res = P_i->eval(a); + + for (unsigned int i = 0; i < auxPolys.size(); i++){ + res *= subspaceValues[auxPolys[i]]+shifts[i]; + } + + FieldElement d = denomsValues[denominatorIndex_]; + + if ((d == ZERO) && (res != ZERO)){ + throw not_a_polynomial_exeption(); + } + return res*d; + + } + + /** + * @brief Returns an upper bound for degree of this polynomial + * when composed with univariate polynomials (as its variables) + * @param degrees vector of univariate polynomial, name it \f$\vec{d}\f$ + * @return a degree \f$D\f$ such that + * \f$ D \ge \deg P\circ\vec{Q} \f$ when \f$P\f$ is the current polynomial, + * each \f$Q_i\f$ is a univariate polynomial of degree \f$d_i\f$ at most, + * and \f$(P\circ\vec{Q})(x) = P(Q_1(x),Q(2),\dots,Q_n(x))\f$ + */ + PolynomialDegree AcspSummandsPolynomial::AcspConstraintSummand::getDegreeBound(const vector& inputDegreesBound)const{ + //we return only the degree of the constraint P_i, it uses all inputs but the first. + //it is assumed the degree of the product of the auxPolys is lower than the degree of Z_S + vector P_inputsDeg; + + //return the degree bound + //if (P_i->getDegreeBound(inputDegreesBound)>0)//ARIEL + //std::cout << inputDegreesBound[0] << inputDegreesBound[1] << inputDegreesBound[2];//ARIEL + //std::cout << "P_i-deg:" << P_i->getDegreeBound(inputDegreesBound) << std::endl; //ARIEL + return P_i->getDegreeBound(inputDegreesBound); + } + + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/AcspSummandsPolynomial.hpp b/libstark/src/reductions/BairToAcsp/Details/AcspSummandsPolynomial.hpp new file mode 100644 index 0000000..5c9f162 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/AcspSummandsPolynomial.hpp @@ -0,0 +1,229 @@ + +#include +#include "algebraLib/SubspacePolynomial.hpp" +#include "common/Algebra/ShiftedSubspacePolynomial.hpp" + +#include +#ifndef REFACTORING_Acsp_AcspSUMMANDSPOLYNOMIAL_HPP__ +#define REFACTORING_Acsp_AcspSUMMANDSPOLYNOMIAL_HPP__ + +namespace libstark{ +namespace BairToAcsp{ + + +/** + * \class AcspSummandsPolynomial + * + * This class is designed to hold a multivariate-polynomial represented as + * \f$ \sum_{i=1}^{n} Sel_i \cdot P_i \f$ where for each \f$i\f$, \f$Sel_i \in \mathbb{F}[x] \f$ + * and \f$ P_i \in \mathbb{F}[\mathcal{V}] \f$ for some variables set \f$ \mathcal{V} \f$ + * (this form arises in the BS/BGHSP PCP). + * Additionally there is another parameter of the polynomial, \f$ H \subset \mathbb{F} \f$ which is a \f$ \mathbb{F}_2 \f$-linear subspace. + * \f$ H \f$ defines a restriction on each \f$ Sel_i \f$ that we will explain later. + * + * We interpret the factors of each summand in the following way: + * + * \f$P_i\f$ - the multivariate polynomial representing the constraint , is a rather general polynomial, but usually very low-degree - typically 2 or 3 (but might be arbitrary). + * + * \f$Sel_i\f$ - the 'Selector polynomial', is a polynomial of relatively high degree\f$^*\f$ but with a lot of structure. + * It's purpose is to vanish on \f$ H \setminus U_i \f$, where \f$U_i\f$ is the set of points on which we wish to verify the constraint \f$P_i\f$. + * \f$U_i\f$ is usually a subspace of \f$H\f$, but might be an arbitrary subset as well. + * More details about the structure of \f$Sel_i\f$ are given below - in the description of the private class AcspConstraintSummand. + * The purpose of this private class is to contain such a summand. + * Then, we can represent the polynomial as a vector of AcspConstraintSummand (see details below on this private class, defined in this file). + * + * Creating a class for this special type of polynomial allows for faster evaluation- especially when + * the evaluation is not just on one point, but on a large space - as required for the PCP proof. + * To be more precise, the polynomial we wish to evaluate quickly is a composition of the polynomial represented here + * with univariate polynomials. + * See AcspComposedPolynomial for details on this composition. + * + * + * \note There is a nuance here (better to ignore it at a first read). Usually in the PCP description \f$Sel_i\f$ is a ratio of two affine polynomials, + * the numerator being the subspace polynomial \f$Z_H\f$ (which is the *same for all summands*); + * but in the actual code - we have \f$Z_H\f$ multiply the whole polynomial, + * and the summand contains only the denumerator part. (There are cases where there are other things in the numerator that come from a parameter called auxPolys) + * + * + * \author Ariel Gabizon - ariel.gabizon@gmail.com, and Michael Riabzev - riabzevmichael@gmail.com + */ + + class AcspSummandsPolynomial : public Algebra::PolynomialInterface{ + class AcspConstraintSummand; + public: + typedef Algebra::PolynomialInterface Constraint; //currently we just have the constraints be general polynomials, but perhaps will change later on + typedef std::vector EvaluationPoint; + typedef unsigned int polyID; + typedef std::vector SubspacePolyVec; + typedef std::pair AuxPoly; //the auxillary polynomials are represented by (an ID of a) subspace polynomial, and its shift + typedef std::vector AuxPolyVec; + typedef std::vector SummandsVector; + + //A denominator is a pair of a linearized polynomial $L$ (represented by its ID) + //and a constant shift $c$ which is a field element. + //This is used because each AcspConstraintSummand has such pair, which while it is + //evaluated on some assignment x,y_1...y_n it should calculate the value of 1/(L(x)-c). + //Inversion is time consuming, but inversion of a bunch of FieldElements together is cheeper, + //so we do all the inversions togeter, and pass the results the the AcspConstraintSummand polynomials. + typedef std::pair DenominatorType; + typedef std::vector DenominatorsVecType; + + /// Creates a new AcspSummandsPolynomial, and sets \f$H\f$ to be \f$ span( spanSetH ) \f$ + AcspSummandsPolynomial(const Algebra::elementsSet_t& spanSetH); + + //Copy constructor + AcspSummandsPolynomial(const AcspSummandsPolynomial& that); + /// Adds a subspace polynomial vanishing on \f$span(T)\f$ into subspacePolys and returns its id, i.e, its index in subspacePolys + /// It is enforced that \f$ span(T) \subset H \f$ (otherwise _COMMON_FATAL is called) + polyID addSubspacePoly(const Algebra::elementsSet_t& T); + + /// evaluates this polynomial on \f$ a \f$ + Algebra::FieldElement eval(const EvaluationPoint& a)const; + + size_t numVars()const; + + //evaluates a polynomial on a given set (represented as vector) of assignments + std::vector evalOnSet(const std::vector>& x_set)const; + + // check if the \f$ a \f$ is a root of the polynomial + bool isRoot(const EvaluationPoint& x)const; + + /// Construct the summand \f$ \frac{1}{Z_S(x-shift)} \cdot P_i \f$. + /// Technically what we construct is the equivalent summand \f$ \frac{1}{Z_S(x) - c} \cdot P_i \f$, where \f$ c=Z_S(shift) \f$ + /// It is enforced that \f$ shift \in H \f$ (otherwise _COMMON_FATAL is called) + void addSummand(const polyID Z_S, const Algebra::FieldElement& shift, const Constraint& P_i); + + /// construct the summand \f$ \frac{1}{Z_S+c} \cdot P_i \cdor W \f$, where \f$c=Z_S(shift)\f$ and \f$W\f$ + /// is the product of polynomials in auxPolys shifted according to the shifts vector + /// It is enforced that \f$ shift \in H \f$ and all the shifts are in \f$ H \f$ as well (otherwise _COMMON_FATAL is called) + void addSummand(const polyID Z_S, const Algebra::FieldElement& shift, const Constraint& P_i, const AuxPolyVec& auxVec); + + /** + * @brief return a clone of the current polynomial + * @return a unique_ptr of PolynomialInterface, + * representing a polynomial equivalent to current + */ + std::unique_ptr clone()const; + + /** + * @brief Returns an upper bound for degree of this polynomial + * when composed with univariate polynomials (as its variables) + * @param degrees vector of univariate polynomial, name it \f$\vec{d}\f$ + * @return a degree \f$D\f$ such that + * \f$ D \ge \deg P\circ\vec{Q} \f$ when \f$P\f$ is the current polynomial, + * each \f$Q_i\f$ is a univariate polynomial of degree \f$d_i\f$ at most, + * and \f$(P\circ\vec{Q})(x) = P(Q_1(x),Q(2),\dots,Q_n(x))\f$ + */ + Algebra::PolynomialDegree getDegreeBound(const std::vector& inputDegreesBound)const; + + /** + * @brief Returns an upper bound for degree of this polynomial divided by ZH + * when composed with univariate polynomials (as its variables) + * @param degrees vector of univariate polynomial, name it \f$\vec{d}\f$ + * @return a degree \f$D\f$ such that + * \f$ D \ge \deg P\circ\vec{Q} \f$ when \f$P\f$ is the current polynomial, + * each \f$Q_i\f$ is a univariate polynomial of degree \f$d_i\f$ at most, + * and \f$(P\circ\vec{Q})(x) = P(Q_1(x),Q(2),\dots,Q_n(x))\f$ + */ + Algebra::PolynomialDegree getDegreeBound_DividedByZH(const std::vector& inputDegreesBound)const; + + /// evaluated this polynomial divided by \f$ Z_H \f$ on \f$ a \f$ + /// \note if \f$ a_0 \in H \f$ the result is not necessarily right, + /// but because we multiply it be the result of \f$ Z_H \f$, it should vanish there any way. + std::vector evalDividedByZH(const std::vector& a, const bool allowEvalIn_H=false)const; + + private: + + ///The subspace polynomial that vanishes over the subspace \f$H\f$ - that the Acsp composed (univariate) polynomial is supposed to vanish on + Algebra::SubspacePolynomial Z_H; + + ///Stores the different possibilities for the polynomials \f$Z_S\f$ or the auxiliary polynomials described below. The point is there are only a few + ///different such polynomials, each appearing in many summands. + SubspacePolyVec subspacePolys; + + //the denominators vector, for a bunch inversion (cheeper than one at a time) + DenominatorsVecType denomsVec_; + + ///stores the summands in this polynomial + SummandsVector summands; + + + /** + * \class The AcspConstraintSummand class + * + * This class holds a single summand of the Acsp constraints polynomial - implemented by the class AcspSummandsPolynomial. + * As described in AcspSummandsPolynomial, the summand is of the form \f$ Sel*P_i \f$ (there we used \f$ Sel_i \f$ rather than \f$ Sel \f$). + * \f$ Sel \f$ is *usually* of the form \f$ Sel = \frac{Z_H}{Z_S+c} \f$ for some constant \f$ c \in H \f$. + * where \f$ S \f$ is (supposed to be) a subspace contained in \f$ H \f$, and \f$ Z_S \f$ is the subspace polynomial vanishing on \f$ S \f$. + * That is, \f$ Sel \f$ vanishes exactly on \f$ H \setminus S+c \f$. + * We note that \f$ Z_H \f$ is not stored in this class, but only in the AcspSummandsPolynomial class. + * As \f$ Z_H \f$ and \f$ Z_S \f$ are subspace polynomials, + * \f$ Sel \f$ can be computed much more efficiently when represented explicitly as this quotient + * (rather than explicitly computing \f$ Sel \f$'s coefficients), especially in our case, when the enumerator is common for a lot of selectors. + * In fact, in the PCP we are interested in computing \f$ Sel \cdot \frac{P_i}{Z_H} = \frac{P_i}{Z_S+c} \f$ composed with univariate polynomials. + * This is another reason it makes sense to exlicitly store \f$ Z_S \f$ and \f$ c \f$, rather than just \f$ Sel \f$'s coefficients. + * Occaisionally, \f$ Sel \f$ will be of the form described above + * but multiplied by a few more auxiliary shifted subspace polynomials. + * This case is addressed by the member variable auxPolys + * + * We stress again, that though in PCP descriptions \f$ Sel = \frac{Z_H}{Z_S+c} \f$ + * what is actually stored here is the summand \frac{1}{Z_S+c} \cdot P_i \f$ - which is not even a poly but a rational function! + * (Though it is supposed to turn into a poly after the univariate substitution derived from a legal assignment) + * and this is what the eval function of the object computes + * + **/ + + class AcspConstraintSummand{ + public: + + /// move constructor + AcspConstraintSummand(AcspConstraintSummand&& rhs); + + /// construct the summand \f$ \frac{1}{Z_S+c} \cdot P_i \f$. + AcspConstraintSummand(const polyID denomIndex, const Constraint& P_i); + + //// construct the summand \f$ \frac{1}{Z_S+c} \cdot P_i \cdot W \f$ , where \f$ W \f$ is the product of polynomials in auxPolys + AcspConstraintSummand(const polyID denomIndex, const Constraint& P_i, const std::vector& auxPolys, const std::vector& shifts); + + /// copy constructor + AcspConstraintSummand(const AcspConstraintSummand& src); + + /// evaluates the summand on a given point \f$ a \f$ + /// \note important point evaluates without the \f$ Z_H \f$ part! + /// + /// it receives as input the array subspaceValues containing the evaluations on \f$ a \f$ of the subspace polys + /// associated with the AcspSummandsPolynomial object + /// + /// \warning This method does not always evaluate the summand exactly, the summand is represented + /// as the rational function \f$ \frac{P_i \cdot W}{Z_S + c} \f$. + /// In the case both the enumerator and the denumerator vanish on the evaluation point \f$ a \f$ + /// the method return \f$ 0 \f$, which is not allways correct, but good enough for our implementation, + Algebra::FieldElement eval(const EvaluationPoint& a, const std::vector& subspaceValues, const std::vector& denomsVals) const; + + /** + * @brief Returns an upper bound for degree of this polynomial + * when composed with univariate polynomials (as its variables) + * @param degrees vector of univariate polynomial, name it \f$\vec{d}\f$ + * @return a degree \f$D\f$ such that + * \f$ D \ge \deg P\circ\vec{Q} \f$ when \f$P\f$ is the current polynomial, + * each \f$Q_i\f$ is a univariate polynomial of degree \f$d_i\f$ at most, + * and \f$(P\circ\vec{Q})(x) = P(Q_1(x),Q(2),\dots,Q_n(x))\f$ + */ + Algebra::PolynomialDegree getDegreeBound(const std::vector& inputDegreesBound)const; + + private: + polyID denominatorIndex_; + std::unique_ptr P_i; + std::vector auxPolys;//stores the auxiliary polynomials + std::vector shifts;//stores the shifts of the auxiliary polynomials + + + };//end of AcspConstraintSummand + + + };//end of AcspSummandsPolynomial + +} //namespace BairToAcsp +} //namespace libstark + +#endif //REFACTORING_Acsp_AcspSUMMANDSPOLYNOMIAL_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/boundaryConstraints.hpp b/libstark/src/reductions/BairToAcsp/Details/boundaryConstraints.hpp new file mode 100644 index 0000000..4d3c7e3 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/boundaryConstraints.hpp @@ -0,0 +1,43 @@ +#ifndef BairToAcsp_INPUTINDICATOR_HPP__ +#define BairToAcsp_INPUTINDICATOR_HPP__ + +namespace libstark{ +namespace BairToAcsp{ + +class boundaryMapping : private instanceMappings{ +public: + boundaryMapping(const common& commonInfo): + instanceMappings(commonInfo){}; + + witnessElement_t mapPoint(const size_t rowId, const size_t varId)const{ + commonMappings::witnessElement_t res = mapVariable_witnessElement(varId); + res.second += mapRowId(rowId); + + return res; + } + +private: + //Methods + Algebra::FieldElement mapRowId(const size_t rowId)const{ + return map_x_power_modulu_poly(rowId, commonInfo_.rowsModulus()); + } +}; + +std::vector reduceBoundaryConstraints(const BairInstance::boundaryConstraints_t& bairBoundary, const common& commonDef){ + boundaryMapping mapping(commonDef); + std::vector boundaryConstraints(commonDef.witnessLayersAmount()); + for(const auto& p : bairBoundary){ + const auto currPoint = p.first; + const size_t rowId = currPoint.first; + const size_t varId = currPoint.second; + const auto x = mapping.mapPoint(rowId,varId); + boundaryConstraints[x.first][x.second] = p.second; + } + + return boundaryConstraints; +} + +} //namespace BairToAcsp +} //namespace libstark + +#endif //#ifndef BairToAcsp_INPUTINDICATOR_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/common.hpp b/libstark/src/reductions/BairToAcsp/Details/common.hpp new file mode 100644 index 0000000..a2c56ef --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/common.hpp @@ -0,0 +1,34 @@ +/** + * @file BEXtoAcsp_common.hpp + * + * Declares the BairToAcsp reduction common, + * as descried at Acsp Spec document. + */ + +#ifndef _COMMON_BairToAcsp_COMMON_HPP_ +#define _COMMON_BairToAcsp_COMMON_HPP_ + +#include "commonDeffinitions.hpp" +#include "commonInformation.hpp" +//#define ONE_CONSTRAINT //ARIEL-For debugging purposes to get to pcp part faster +namespace libstark{ +namespace BairToAcsp{ + +/* + * @class common + * @brief common values, both well defined and those that have some freedom, united in one class + */ +class common : public commonDeffinitions, public commonInformation{ + public: + common(const BairInstance& instance): + commonDeffinitions(instance), + commonInformation(*((commonDeffinitions*)this)) + {}; + + common(const common&) = delete; +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //_COMMON_BairToAcsp_COMMONINFORMATION_HPP_ diff --git a/libstark/src/reductions/BairToAcsp/Details/commonDeffinitions.cpp b/libstark/src/reductions/BairToAcsp/Details/commonDeffinitions.cpp new file mode 100644 index 0000000..99c5171 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/commonDeffinitions.cpp @@ -0,0 +1,153 @@ +#include "commonDeffinitions.hpp" +#include + +using std::vector; +using std::ceil; +using std::max; +using std::pair; +using std::count; +using std::move; +using std::unique_ptr; +using Infrastructure::Log2; +using Infrastructure::POW2; +using Algebra::FieldElement; +using Algebra::PolynomialDegree; +using Algebra::PolynomialInterface; +using Algebra::UnivariatePolynomialGeneral; +using Algebra::getStandartBasis; +using Algebra::zero; + +namespace libstark{ +namespace BairToAcsp{ + + commonDeffinitions::commonDeffinitions(const BairInstance& instance) { + + //getting variables partition + { + variablesPerm_ = getRoutedIndexes(instance); + auto vars_n_constraints = getUnroutedIndexes_and_ConstraintsChi(instance); + variablesNonPerm_ = vars_n_constraints.first; + constraintsChi_ = move(vars_n_constraints.second); + + //initialize constraints Pi + { + const vector coeffs(instance.constraintsPermutation().numMappings(),Algebra::one()); + constraintsPi_.push_back(unique_ptr(instance.constraintsPermutation().getLinearComb(coeffs))); + } + } + + //get the variables location translation + { + bairVarLocation_.resize(instance.vectorsLen()); + + for(size_t i=0; i 0){ + widthSpaceDimension_ = ceil(Log2(instance.domainSizeIndicator()+2)); + } + + amountOfPermutationLayers_ = 0; + if(variablesPerm_.size() > 0){ + amountOfPermutationLayers_ = 2*(1+variablesPerm_.size()); + } + + const double spaceWidth = POW2(widthSpaceDimension_); + { + const double totalConstraintsAmount = 1 + 1;//instance.constraintsAssignment().constraints().size() + instance.constraintsPermutation().constraints().size(); + const size_t vanishingLayersConstraints = ceil(totalConstraintsAmount/spaceWidth); + vanishingLayersSpaceDimension_ = ceil(Log2(amountOfPermutationLayers_ + vanishingLayersConstraints)); + } + + { + const size_t numOfVariablesLayers = ceil(double(variablesNonPerm_.size())/double(spaceWidth)); + numOfWitnessLayers_ = amountOfPermutationLayers_ + numOfVariablesLayers; + } + + { + const size_t spaceWidth = POW2(widthSpaceDimension_); + const size_t witnessDegreeFactor = (SUPPORT_ZK?2:1); + witnessDegreeBound_ = vector(numOfWitnessLayers_,PolynomialDegree(witnessDegreeFactor*POW2(heightSpaceDimension_)*spaceWidth-1)); + } +} + +PolynomialDegree commonDeffinitions::getMaxTestDegree(const BairInstance& instance, const bool hasRoutingNetwork, const PolynomialDegree& ascpWitnessDegreeBound){ + + // initialize a vector of zeros as input degrees, + // for checking the maximal degree of a polynomial + // in each constraint system + const size_t assignmetnSize = 2*instance.vectorsLen(); + vector inputDegrees(assignmetnSize,ascpWitnessDegreeBound); + + // find maximal degree in constraint system tests + PolynomialDegree maxDeg = max(instance.constraintsAssignment().getMaximalDegree(),instance.constraintsPermutation().getMaximalDegree()); + + // find maximal in case there is a routing network + // the maximal degree test for routing is 2 (specifically booleanity testing) + if(hasRoutingNetwork){ + maxDeg = max(maxDeg,PolynomialDegree(2)); + } + + // return maximal degree + return maxDeg; +} + + +// Routing +vector commonDeffinitions::getRoutedIndexes(const BairInstance& instance){ + const auto vecLen = instance.vectorsLen(); + const auto& CSystemPermutation = instance.constraintsPermutation(); + + vector usedIndexes; + //Push only the indexes that must be routed + //meaning, only those that the constraints system uses + //from the second vector + for(size_t i=vecLen; i< 2*vecLen; i++){ + if(CSystemPermutation.varUsed(i)){ + usedIndexes.push_back(i-vecLen); + } + } + + return usedIndexes; +} + +pair,vector>> commonDeffinitions::getUnroutedIndexes_and_ConstraintsChi(const BairInstance& instance){ + const auto vecLen = instance.vectorsLen(); + const auto& CSystemPermutation = instance.constraintsPermutation(); + + vector unusedIndexes; + //Push only the indexes that are not routed + //meaning, only those that the constraints system not uses + //from the second vector + for(size_t i=vecLen; i< 2*vecLen; i++){ + if(!CSystemPermutation.varUsed(i)){ + unusedIndexes.push_back(i-vecLen); + } + } + + //if there are any univariate constraints, + //reorder the variable, so those used by the most + //common univariate constraints are all at the beginning + vector reorderedUnusedIndexes; + vector> reorderedConstraints; + reorderedUnusedIndexes = unusedIndexes; + { + vector coeffs(instance.constraintsAssignment().numMappings(),Algebra::one()); + reorderedConstraints.push_back(unique_ptr(instance.constraintsAssignment().getLinearComb(coeffs))); + } + + return pair,vector>>(reorderedUnusedIndexes,move(reorderedConstraints)); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/commonDeffinitions.hpp b/libstark/src/reductions/BairToAcsp/Details/commonDeffinitions.hpp new file mode 100644 index 0000000..7f72ec7 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/commonDeffinitions.hpp @@ -0,0 +1,113 @@ +/** + * @file BEXtoAcsp_commonDeffinitions.hpp + * + * Declares the BairToAcsp reduction common definitions, + * as descried at Acsp Spec document. + */ + +#ifndef _COMMON_BairToACCSP_COMMONDEFINITIONS_HPP_ +#define _COMMON_BairToACCSP_COMMONDEFINITIONS_HPP_ + +#include "languages/Bair/BairInstance.hpp" +#include + +namespace libstark{ +namespace BairToAcsp{ + +const bool SUPPORT_ZK = true; + +/** + * @class commonDeffinitions + * @brief represents known values commonly used in the reduction, and depend only on the partial instance + * + * We use the BairInstance : \f$(\mathbb{F},d,\mathcal{V},\mathcal{C}_\mathcal{A}, \mathcal{C}_\pi, B)\f$ + */ +class commonDeffinitions{ + +public: + + struct varLocation{ + bool inPerm; + size_t index; + }; + + //constructor + commonDeffinitions(const BairInstance& instance); + + /* + * variables partition getters + */ + + /// return the vector of ids of variables that must be passed through the routing network + /// formally , this is the vector of variable \f$ v \in \mathcal{V} \f$ + /// such that \f$ (v,1) \in [\mathcal{V} \times \{1\}](\mathcal{C}_\pi) \f$ + inline const std::vector& variablesPerm()const{return variablesPerm_;} + + /// returns the vector of ids of variable that not need to be passed through the routing network + /// formally , this is the vector of variable \f$ v \in \mathcal{V} \f$ + /// such that \f$ (v,1) \notin [\mathcal{V} \times \{1\}](\mathcal{C}_\pi) \f$ + inline const std::vector& variablesNonPerm()const{return variablesNonPerm_;} + + /// returns the location of the variable with given id, + /// the location says in which variables vector it is found (permutation variable or not) + /// and what is its index there. + /// + /// for example, if getVarLocation(v) = {inPerm = true, index = i}, than + /// variablesPerm()[i] = v + /// + /// and if getVarLocation(u) = {inPerm = false, index = j}, than + /// variablesNonPerm()[j] = u + inline const varLocation& getVarLocation(const size_t varId)const{return bairVarLocation_[varId];} + + /* + * dimensions and sizes getters + */ + inline const size_t& heightSpaceDimension()const{return heightSpaceDimension_;} + inline const size_t imageHeight()const{return Infrastructure::POW2(heightSpaceDimension_);} + inline const size_t& widthSpaceDimension()const{return widthSpaceDimension_;} + inline const size_t imageWidth()const{return Infrastructure::POW2(widthSpaceDimension_);} + inline const size_t& amountOfPermutationLayers()const{return amountOfPermutationLayers_;} + inline const size_t& vanishingLayersSpaceDimension()const{return vanishingLayersSpaceDimension_;} + inline const size_t vanishingLayersAmount()const{return Infrastructure::POW2(vanishingLayersSpaceDimension_);} + inline const size_t witnessLayersAmount()const{return numOfWitnessLayers_;} + inline size_t vanishingSpaceDimension()const{return heightSpaceDimension()+widthSpaceDimension()+vanishingLayersSpaceDimension();} + inline bool hasRoutingNetwork()const{return variablesPerm_.size()>0;} + inline const std::vector& witnessDegreeBound()const{return witnessDegreeBound_;} + + //constraints + const std::vector>& getConstraintsChi()const{return constraintsChi_;} + const std::vector>& getConstraintsPi()const{return constraintsPi_;} + + //deleted functions + commonDeffinitions(const commonDeffinitions&) = delete; +private: + + //variables partition + std::vector variablesPerm_; + std::vector variablesNonPerm_; + std::vector bairVarLocation_; + + //the constraints for CHI ordered + std::vector> constraintsChi_; + std::vector> constraintsPi_; + + //dimensions and sizes + size_t heightSpaceDimension_; + size_t widthSpaceDimension_; + size_t amountOfPermutationLayers_; + size_t vanishingLayersSpaceDimension_; + size_t vanishingSpaceDimension_; + size_t numOfWitnessLayers_; + std::vector witnessDegreeBound_; + + //private methods + static std::vector getRoutedIndexes(const BairInstance& instance); + static std::pair,std::vector>> getUnroutedIndexes_and_ConstraintsChi(const BairInstance& instance); + static Algebra::PolynomialDegree getMaxTestDegree(const BairInstance& instance, const bool hasRoutingNetwork, const Algebra::PolynomialDegree& ascpWitnessDegreeBound); + static std::pair > > getCommonUnivariateConstraint(const BairInstance& instance); +}; + +} //namespace BairToAcsp +} // namespace libstark + +#endif //_COMMON_BairToACCSP_COMMONDEFINITIONS_HPP_ diff --git a/libstark/src/reductions/BairToAcsp/Details/commonInformation.hpp b/libstark/src/reductions/BairToAcsp/Details/commonInformation.hpp new file mode 100644 index 0000000..461ea26 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/commonInformation.hpp @@ -0,0 +1,124 @@ +/** + * @file BEXtoAcsp_commonInforamtion.hpp + * + * Declares the BairToAcsp reduction common information, + * as descried at Acsp Spec document. + */ + +#ifndef _COMMON_BairToAcsp_COMMONINFORMATION_HPP_ +#define _COMMON_BairToAcsp_COMMONINFORMATION_HPP_ + +#include "commonDeffinitions.hpp" +#include "common/Utils/ErrorHandling.hpp" + +namespace libstark{ +namespace BairToAcsp{ + + +/** + * @class commonInformation + * @brief represents common values that are restricted, but may be chosen with some freedom + * + * We use the BairPartialInstance : \f$(\mathbb{F},d,\mathcal{V},\mathcal{C}_\mathcal{A}, \mathcal{C}_\pi, \mathscr{I})\f$ + */ +class commonInformation{ +public: + commonInformation(const commonDeffinitions& commonDef): + rowsPrimitivePoly_(findPrimitive(commonDef.heightSpaceDimension())), + columnsPrimitivePoly_(findPrimitive(commonDef.widthSpaceDimension())) + { }; + + /// The modulus for row ids field simulation + const int64_t rowsModulus()const {return rowsPrimitivePoly_;} + + /// The modulus for column ids field simulation + const int64_t columnsModulus()const {return columnsPrimitivePoly_;} + +private: + const int64_t rowsPrimitivePoly_; + const int64_t columnsPrimitivePoly_; + +/** +* Returns a primitive polynomial +* The primitive polynomial is found in a lookup table, according to the requested degree. +*/ +static int64_t findPrimitive(const int degree) { + + int64_t polyRepresentation; + + //Step 1 - Finding the representation of the polynomial in the lookup table: + switch(degree) { + case 0: polyRepresentation = 01; break; + case 1: polyRepresentation = 02; break; + case 2: polyRepresentation = 07; break; + case 3: polyRepresentation = 013; break; + case 4: polyRepresentation = 023; break; + case 5: polyRepresentation = 045; break; + case 6: polyRepresentation = 0103; break; + case 7: polyRepresentation = 0203; break; + case 8: polyRepresentation = 0435; break; + case 9: polyRepresentation = 01021; break; + case 10: polyRepresentation = 02011; break; + case 11: polyRepresentation = 04005; break; + case 12: polyRepresentation = 010123; break; + case 13: polyRepresentation = 020033; break; + case 14: polyRepresentation = 042103; break; + case 15: polyRepresentation = 0100003; break; + case 16: polyRepresentation = 0210013; break; + case 17: polyRepresentation = 0400011; break; + case 18: polyRepresentation = 01000201; break; + case 19: polyRepresentation = 02000047; break; + case 20: polyRepresentation = 04000011; break; + case 21: polyRepresentation = 010000005; break; + case 22: polyRepresentation = 020000003; break; + case 23: polyRepresentation = 040000041; break; + case 24: polyRepresentation = 0100000207; break; + case 25: polyRepresentation = 0200000011; break; + case 26: polyRepresentation = 0400000107; break; + case 27: polyRepresentation = 01000000047; break; + case 28: polyRepresentation = 02000000011; break; + case 29: polyRepresentation = 04000000005; break; + case 30: polyRepresentation = 010040000007; break; + case 31: polyRepresentation = 020000000011; break; + case 32: polyRepresentation = 040020000007; break; + case 33: polyRepresentation = 0100000020001; break; + case 34: polyRepresentation = 0201000000007; break; + case 35: polyRepresentation = 0400000000005; break; + case 36: polyRepresentation = 01000000004001; break; + case 37: polyRepresentation = 02000000012005; break; + case 38: polyRepresentation = 04000000000143; break; + case 39: polyRepresentation = 010000000000021; break; + case 40: polyRepresentation = 020000012000005; break; + case 41: polyRepresentation = 0x20000000009; break; + case 42: polyRepresentation = 0x404c0000001; break; + case 43: polyRepresentation = 0x80008400021; break; + case 44: polyRepresentation = 0x108800040001; break; + case 45: polyRepresentation = 0x208010000011; break; + case 46: polyRepresentation = 0x410080040001; break; + case 47: polyRepresentation = 0x800000000021; break; + case 48: polyRepresentation = 0x1000000080203; break; + case 49: polyRepresentation = 0x2000000000201; break; + case 50: polyRepresentation = 0x4000480020001; break; + case 51: polyRepresentation = 0x8400001008001; break; + case 52: polyRepresentation = 0x10000000000009; break; + case 53: polyRepresentation = 0x24020000100001; break; + case 54: polyRepresentation = 0x62000020000001; break; + case 55: polyRepresentation = 0x80000001000001; break; + case 56: polyRepresentation = 0x100028020000001; break; + case 57: polyRepresentation = 0x200000000000081; break; + case 58: polyRepresentation = 0x400000000080001; break; + case 59: polyRepresentation = 0x840400004000001; break; + case 60: polyRepresentation = 0x1000000000000003; break; + + default: _COMMON_FATAL("No primitive polynomial found for the requested degree"); + } + + return polyRepresentation; +} + +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //_COMMON_BairToAcsp_COMMONINFORMATION_HPP_ diff --git a/libstark/src/reductions/BairToAcsp/Details/commonMappings.cpp b/libstark/src/reductions/BairToAcsp/Details/commonMappings.cpp new file mode 100644 index 0000000..3ab20ad --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/commonMappings.cpp @@ -0,0 +1,154 @@ +#include "commonMappings.hpp" + +using Algebra::FieldElement; +using Algebra::mapIntegerToFieldElement; +using Infrastructure::Log2; +using std::max; +using std::vector; + +namespace libstark{ +namespace BairToAcsp{ + +commonMappings::commonMappings(const common& commonInfo): + bitsForRowId_(commonInfo.heightSpaceDimension()), + bitsForColumnId_(commonInfo.widthSpaceDimension()), + bitsForLayerId_(Algebra::ExtensionDegree - (commonInfo.heightSpaceDimension()+commonInfo.widthSpaceDimension())), + bitsForNonPermutationElementId_(Algebra::ExtensionDegree - commonInfo.heightSpaceDimension()), + firstNonPermUsableIndex_(commonInfo.imageWidth()*commonInfo.amountOfPermutationLayers()), + rowsModulus_(commonInfo.rowsModulus()), + column_spaceIndex_(column_spaceIndex_init(commonInfo.columnsModulus(),commonInfo.heightSpaceDimension())){}; + +FieldElement commonMappings::mapNonPermutationElement(const size_t elementId)const{ + return mapIntegerToFieldElement(bitsForRowId_,bitsForNonPermutationElementId_, firstNonPermUsableIndex_ + elementId); +} + +commonMappings::witnessElement_t commonMappings::mapNonPermutationElement_witness(const size_t elementId)const{ + const spaceIndex_t spaceIdx = (firstNonPermUsableIndex_ + elementId) << bitsForRowId_; + return map_spaceIndex_to_witnessElement(spaceIdx); +} + +commonMappings::spaceIndex_t commonMappings::mapNonPermutationVariable_spaceIndex(const size_t elementId)const{ +//#define USE_MICHAELS_NEIGHBORS_TRICK +#ifdef USE_MICHAELS_NEIGHBORS_TRICK + const size_t elementId_clearedLSB = (elementId>>1)<<1; + const spaceIndex_t offset = (elementId%2 == 0? 0 : rowsModulus_); + return ((firstNonPermUsableIndex_ + elementId_clearedLSB)<>numBits_spaceIndex; + const size_t spaceIdx = i & spaceIdx_mask; + + return witnessIndex_t(layerIdx,spaceIdx); +} + +commonMappings::witnessElement_t commonMappings::map_spaceIndex_to_witnessElement(const spaceIndex_t& i)const{ + const witnessIndex_t idx = map_spaceIndex_to_witnessIndex(i); + return witnessElement_t(idx.first, map_spaceIndex_to_fieldElement(idx.second)); +} + +vector commonMappings::column_spaceIndex_init(const int64_t columnsModulus, const size_t shiftSize){ + const int modulusDeg = floor(Infrastructure::Log2(columnsModulus)); + const spaceIndex_t overflow_mask = Infrastructure::POW2(modulusDeg); + + vector res; + res.push_back(1L<>shiftSize)<<1; + if (nextElem & overflow_mask){ + nextElem ^= columnsModulus; + } + + //if we got back to 1, we finished going over all the elements + if(nextElem == 1){ + return res; + } + + //add curr result + res.push_back(nextElem< witnessIndex_t; + typedef std::pair witnessElement_t; + + commonMappings(const common& commonInfo); + Algebra::FieldElement mapNonPermutationElement(const size_t elementId)const; + witnessElement_t mapNonPermutationElement_witness(const size_t elementId)const; + + spaceIndex_t mapPermutationColumnId_spaceIndex(const size_t columnId)const; + Algebra::FieldElement mapPermutationColumnId_spaceElement(const size_t columnId)const; + + spaceIndex_t mapPermutationLayerId_spaceIndex(const size_t layerId)const; + Algebra::FieldElement mapPermutationLayerId_spaceElement(const size_t layerId)const; + + spaceIndex_t mapPermutationElement_spaceIndex(const size_t columnId, const size_t layerId)const; + Algebra::FieldElement mapPermutationElement_spaceElement(const size_t columnId, const size_t layerId)const; + witnessElement_t mapPermutationElement_witnessElement(const size_t columnId, const size_t layerId)const; + /// + /// Let \f$ \eta \f$ be the rows modulus, of degree \f$ d \f$ + /// This function maps the elementId \f$ \sum_{i=0}^{n} b_i 2^i \f$ + /// to the field element \f$ b_0 \cdot \eta + x^d \cdot \sum_{i=1}^{n} b_i x^i \f$ + /// + /// For more details ask Michael Riabzev (this method reduces maximal number of neighbors) + /// + spaceIndex_t mapNonPermutationVariable_spaceIndex(const size_t elementId)const; + Algebra::FieldElement mapNonPermutationVariable_spaceElement(const size_t elementId)const; + witnessElement_t mapNonPermutationVariable_witnessElement(const size_t elementId)const; + + Algebra::FieldElement map_spaceIndex_to_fieldElement(const spaceIndex_t& i)const; + witnessIndex_t map_spaceIndex_to_witnessIndex(const spaceIndex_t& i)const; + witnessElement_t map_spaceIndex_to_witnessElement(const spaceIndex_t& i)const; + +protected: + const size_t bitsForRowId_; + const size_t bitsForColumnId_; + const size_t bitsForLayerId_; + const size_t bitsForNonPermutationElementId_; + const size_t firstNonPermUsableIndex_; + const spaceIndex_t rowsModulus_; + const std::vector column_spaceIndex_; + + static Algebra::FieldElement map_x_power_modulu_poly(const size_t x_power, const int64_t modulus); + static std::vector column_spaceIndex_init(const int64_t columnsModulus, const size_t shiftSize); +}; + +} //namespace BairToAcsp +} //namespace libstark + + +#endif // _COMMON_BairToAcsp_COMMONMAPPINGS_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/constraintSystemsTestLocations.cpp b/libstark/src/reductions/BairToAcsp/Details/constraintSystemsTestLocations.cpp new file mode 100644 index 0000000..f80e967 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/constraintSystemsTestLocations.cpp @@ -0,0 +1,27 @@ +#include "constraintSystemsTestLocations.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +CS_testLocations::CS_testLocations(const common& commonDef){ + size_t currIndex = 0; + + for(size_t i=0; i< commonDef.getConstraintsPi().size();i++){ + indexesPermutation_[i] = currIndex++; + } + + for(size_t i=0; i< commonDef.getConstraintsChi().size();i++){ + indexesAssignment_[i] = currIndex++; + } +} + +size_t CS_testLocations::indexOfConstraint_Assignment(polynomialIndicator_t poly)const{ + return indexesAssignment_.at(poly); +} + +size_t CS_testLocations::indexOfConstraint_Permuation(polynomialIndicator_t poly)const{ + return indexesPermutation_.at(poly); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/constraintSystemsTestLocations.hpp b/libstark/src/reductions/BairToAcsp/Details/constraintSystemsTestLocations.hpp new file mode 100644 index 0000000..c1f662e --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/constraintSystemsTestLocations.hpp @@ -0,0 +1,26 @@ +#ifndef CONSTRAINTSYSTEMS_TEST_LOCATIONS_HPP__ +#define CONSTRAINTSYSTEMS_TEST_LOCATIONS_HPP__ + +#include "common.hpp" +#include + +namespace libstark{ +namespace BairToAcsp{ + +class CS_testLocations{ +public: + typedef size_t polynomialIndicator_t; + CS_testLocations(const common& commonDef); + size_t indexOfConstraint_Assignment(polynomialIndicator_t poly)const; + size_t indexOfConstraint_Permuation(polynomialIndicator_t poly)const; + +private: + std::map indexesAssignment_; + std::map indexesPermutation_; +}; + +} //namespace BairToAcsp +} //namespace libstark + + +#endif // #ifndef CONSTRAINTSYSTEMS_TEST_LOCATIONS_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/constraints.cpp b/libstark/src/reductions/BairToAcsp/Details/constraints.cpp new file mode 100644 index 0000000..9ce1848 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/constraints.cpp @@ -0,0 +1,637 @@ +#include "constraints.hpp" +#include "AcspSummandsPolynomial.hpp" + +#include +#include +#include +#include + +namespace libstark{ +namespace BairToAcsp{ + +using Algebra::FieldElement; +using Algebra::zero; +using Algebra::one; +using Algebra::mapIntegerToFieldElement; +using Algebra::PolynomialInterface; +using Algebra::PolynomialDegree; + +using std::vector; +using std::map; +using std::max; +using std::unique_ptr; + +namespace{ + void addPermutationCS_checks( +AcspSummandsPolynomial& bigPoly, +const vector>& constraints, +const AcspSummandsPolynomial::polyID& columnVanishingPoly, +const AcspSummandsPolynomial::polyID& idPoly, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const CS_testLocations& testLocations); + + void addAssignmentCS_checks( +AcspSummandsPolynomial& bigPoly, +const vector>& constraints, +const AcspSummandsPolynomial::polyID& idPoly, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const CS_testLocations& testLocations); + + void addRoutingNetwork_checks( +AcspSummandsPolynomial& bigPoly, +const AcspSummandsPolynomial::polyID& columnVanishingPoly, +const AcspSummandsPolynomial::polyID& idPoly, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const vector& paddingPi); +} + +/** + * Construct the constraints polynomial from a Bair instance. + * This function does the actual work of wiring the constraints-polynomial arithmetic circuit, using some helper functions for sub-circuits. + */ +unique_ptr constraints::getConstraintsPoly( +const BairInstance& instance, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const CS_testLocations& testLocations){ + + // initialize result polynomial + AcspSummandsPolynomial result(spacesGenerator.getVanishingSpaceBasis()); + + // add commonly used subspace polynomials + const auto columnVanishingPoly = result.addSubspacePoly(spacesGenerator.getNetworkColumnBasis()); + const auto idPoly = result.addSubspacePoly(Algebra::elementsSet_t()); //vanishing on span( empty set ) + + //embed the Permutation constraint system checks + addPermutationCS_checks( + result, + commonDef.getConstraintsPi(), + columnVanishingPoly, + idPoly, + neighbors, + commonDef, + instanceMapping, + spacesGenerator, + testLocations); + + //embed the Assignment constraint system checks + addAssignmentCS_checks( + result, + commonDef.getConstraintsChi(), + idPoly, + neighbors, + commonDef, + instanceMapping, + spacesGenerator, + testLocations); + + + /****************************************************************** + * embed the Routing Network checks (if exists) + ******************************************************************/ + if(commonDef.hasRoutingNetwork()){ + addRoutingNetwork_checks( + result, + columnVanishingPoly, + idPoly, + neighbors, + commonDef, + instanceMapping, + spacesGenerator, + instance.paddingPi()); + + } + + return result.clone(); + +} + +namespace{ +class sparceInputsPoly : public PolynomialInterface{ +public: + sparceInputsPoly(const PolynomialInterface& poly, const map varsTranslation, const size_t inputsNum): + poly_(poly.clone()),inputsNum_(inputsNum){ + + size_t maxUsed = 0; + for(const auto& translation : varsTranslation){ + varsTranslation_.push_back(translation); + maxUsed = std::max(maxUsed,translation.first); + } + + assignemntLen_ = maxUsed+1; + }; + + FieldElement eval(const vector& x)const{ + vector assignment(assignemntLen_); + for(const auto& translation : varsTranslation_){ + assignment[translation.first] = x[translation.second]; + } + return poly_->eval(assignment); + } + + size_t numVars()const{ + return inputsNum_; + } + + bool isRoot(const vector& x)const{ + vector miniAssignment(inputsNum_, zero()); + for(const auto& translation : varsTranslation_){ + miniAssignment[translation.first] = x[translation.second]; + } + return poly_->isRoot(miniAssignment); + } + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + vector miniInputDegrees(inputsNum_,PolynomialDegree(0)); + for(const auto& translation : varsTranslation_){ + miniInputDegrees[translation.first] = inputDegrees[translation.second]; + } + return poly_->getDegreeBound(miniInputDegrees); + } + + unique_ptr clone()const{ + map varsTransMap; + for(const auto& translation : varsTranslation_){ + varsTransMap[translation.first] = translation.second; + } + return unique_ptr(new sparceInputsPoly(*poly_,varsTransMap,inputsNum_)); + } + + bool isEffectiveInput(const size_t varId)const{ + for(const auto& translation : varsTranslation_){ + if ((translation.second == varId) && (poly_->isEffectiveInput(translation.first))){ + return true; + } + } + return false; + } + +private: + const unique_ptr poly_; + vector> varsTranslation_; + const size_t inputsNum_; + size_t assignemntLen_; +}; + +class booleanityTestPoly : public PolynomialInterface{ +public: + booleanityTestPoly(const size_t varToTest):varToTest_(varToTest){}; + + FieldElement eval(const vector& x)const{ + const FieldElement& val = x[varToTest_]; + return val*(val-one()); + } + + size_t numVars()const{ + return varToTest_+1; + } + + bool isRoot(const vector& x)const{ + const FieldElement& val = x[varToTest_]; + return (val==one()) || (val==zero()); + } + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + return degreeOfProduct(inputDegrees[varToTest_],inputDegrees[varToTest_]); + } + + unique_ptr clone()const{ + return unique_ptr(new booleanityTestPoly(varToTest_)); + } + + bool isEffectiveInput(const size_t varId)const{ + return varId == varToTest_; + } + +private: + const size_t varToTest_; +}; + +class equivalenceTestPoly : public PolynomialInterface{ +public: + equivalenceTestPoly(const size_t var1, const size_t var2):var1_(var1),var2_(var2){}; + + FieldElement eval(const vector& x)const{ + return x[var1_] - x[var2_]; + } + + size_t numVars()const{ + return max(var1_,var2_)+1; + } + + bool isRoot(const vector& x)const{ + return x[var1_] == x[var2_]; + } + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + return max(inputDegrees[var1_],inputDegrees[var2_]); + } + + unique_ptr clone()const{ + return unique_ptr(new equivalenceTestPoly(var1_,var2_)); + } + + bool isEffectiveInput(const size_t varId)const{ + return (varId == var1_) || (varId == var2_); + } + +private: + const size_t var1_; + const size_t var2_; +}; + +class constValTestPoly : public PolynomialInterface{ +public: + constValTestPoly(const size_t var, const FieldElement& val):var_(var),val_(val){}; + + FieldElement eval(const vector& x)const{ + return x[var_] - val_; + } + + size_t numVars()const{ + return var_+1; + } + + bool isRoot(const vector& x)const{ + return x[var_] == val_; + } + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + return inputDegrees[var_]; + } + + unique_ptr clone()const{ + return unique_ptr(new constValTestPoly(var_,val_)); + } + + bool isEffectiveInput(const size_t varId)const{ + return varId == var_; + } + +private: + const size_t var_; + const FieldElement val_; +}; + +class DeBruijnRoutingTestPoly : public PolynomialInterface{ +public: + DeBruijnRoutingTestPoly(const size_t currVal_var, const size_t neighbor0_var, const size_t neighbor1_var, const size_t routingBit_var): + currVal_var_(currVal_var), + neighbor0_var_(neighbor0_var), + neighbor1_var_(neighbor1_var), + routingBit_var_(routingBit_var){}; + + FieldElement eval(const vector& x)const{ + const FieldElement& v = x[currVal_var_]; + const FieldElement& n0 = x[neighbor0_var_]; + const FieldElement& n1 = x[neighbor1_var_]; + const FieldElement& b = x[routingBit_var_]; + + /******************************************************************** + * Construct the test + * + * The test is just the polynomial: + * Let \f$ b \f$ be the value of the routing bit, + * \f$ v \f$ be the value of in current location, + * and \f$ n_0, n_1 \f$ be the values in the both neighbours. + * The test should verify that if \f$ b = 0 \f$ than \f$ v = n_0 \f$ + * and otherwise if \f$ b = 1 \f$ than \f$ v=n_1 \f$. + * (It is verified using the booleanity test that \f$ b \in \{0,1\} \f$) + * + * It does it using the polynomial (over a field of characteristic 2 only): + * \f$ b \cdot (v+n_1) + (1+b) \cdot (v+n_0) = + * b \cdot ( v+n_1 + v+n_0 ) + (v+n_0) = + * b \cdot ( n_0 + n_1) + v + n_0 \f$ + * *****************************************************************/ + return b*(n0 + n1) + v + n0; + } + + size_t numVars()const{ + return max(max(currVal_var_, routingBit_var_), max(neighbor0_var_,neighbor1_var_))+1; + } + + PolynomialDegree getDegreeBound(const vector& inputDegrees)const{ + const PolynomialDegree& v = inputDegrees[currVal_var_]; + const PolynomialDegree& n0 = inputDegrees[neighbor0_var_]; + const PolynomialDegree& n1 = inputDegrees[neighbor1_var_]; + const PolynomialDegree& b = inputDegrees[routingBit_var_]; + + /// degree of \f$ b \cdot (v - n_0) \f$ + const PolynomialDegree d0 = degreeOfProduct(b,max(v,n0)); + + /// degree of \f$ b \cdot (v - n_1) \f$ + const PolynomialDegree d1 = degreeOfProduct(b,max(v,n1)); + + return max(d0,d1); + } + + unique_ptr clone()const{ + return unique_ptr(new DeBruijnRoutingTestPoly(*this)); + } + + bool isEffectiveInput(const size_t varId)const{ + return (varId == currVal_var_) + || + (varId == neighbor0_var_) + || + (varId == neighbor1_var_) + || + (varId == routingBit_var_); + } + +private: + const size_t currVal_var_; + const size_t neighbor0_var_; + const size_t neighbor1_var_; + const size_t routingBit_var_; +}; + +void addAssignmentCS_checks( +AcspSummandsPolynomial& bigPoly, +const vector>& constraints, +const AcspSummandsPolynomial::polyID& idPoly, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const CS_testLocations& testLocations){ + + const auto columnVanishingPoly_noCarry = bigPoly.addSubspacePoly(spacesGenerator.getNetworkColumnAssignmentNoCarryBasis()); + const size_t chiCarryBitLocation = commonDef.heightSpaceDimension() - 1; + const FieldElement offsetForWithCarrySpace = mapIntegerToFieldElement(chiCarryBitLocation,1,1); + + const size_t assignmentSize = 2*(commonDef.variablesPerm().size() + commonDef.variablesNonPerm().size()); + + // each polynomial in the constraint system find right assignment for it, + //and multiply the result by a selector for current location + for(size_t poly_indx=0; poly_indx inputTranslation_noCarry; + map inputTranslation_withCarry; + bool usesVarFromNextLine = false; + for(size_t varId=0; varId< assignmentSize; varId++){ + + //if the variable is used, get next input from the global inputs + if(neighbors.existsAssignmentCS(poly_indx,varId)){ + inputTranslation_noCarry [varId] = 1 + neighbors.locationOfAssignmentCS(poly_indx,varId,0); + inputTranslation_withCarry[varId] = 1 + neighbors.locationOfAssignmentCS(poly_indx,varId,1); + if (varId > assignmentSize/2){ + usesVarFromNextLine = true; + } + } + } + + // construct the constraint polynomial + const sparceInputsPoly constraintPoly_noCarry(*poly, inputTranslation_noCarry, assignmentSize); + const sparceInputsPoly constraintPoly_withCarry(*poly, inputTranslation_withCarry, assignmentSize); + + // + // Add the summand + // + + bigPoly.addSummand(columnVanishingPoly_noCarry, testShift, constraintPoly_noCarry, auxPolys_noCarry); + if(usesVarFromNextLine){ + bigPoly.addSummand(columnVanishingPoly_noCarry, offsetForWithCarrySpace + testShift, constraintPoly_withCarry, auxPolys_withCarry); + } + } +} + +void addPermutationCS_checks( +AcspSummandsPolynomial& bigPoly, +const vector>& constraints, +const AcspSummandsPolynomial::polyID& columnVanishingPoly, +const AcspSummandsPolynomial::polyID& idPoly, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const CS_testLocations& testLocations){ + + const size_t assignmentSize = 2*(commonDef.variablesPerm().size() + commonDef.variablesNonPerm().size()); + + // each polynomial in the constraint system find right assignment for it, + //and multiply the result by a selector for current location + for(size_t poly_indx=0; poly_indx inputTranslation; + for(size_t varId=0; varId< assignmentSize; varId++){ + + //if the variable is used, get next input from the global inputs + if(neighbors.existsPermCS(poly_indx,varId)){ + inputTranslation[varId] = 1 + neighbors.locationOfPermCS(poly_indx,varId); + } + } + + // construct the constraint polynomial + const sparceInputsPoly constraintPoly(*poly, inputTranslation, assignmentSize); + + // + // Add the summand + // + + bigPoly.addSummand(columnVanishingPoly, testShift, constraintPoly, auxPolys); + } +} + +void addRoutingNetwork_checks( +AcspSummandsPolynomial& bigPoly, +const AcspSummandsPolynomial::polyID& columnVanishingPoly, +const AcspSummandsPolynomial::polyID& idPoly, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const vector& paddingPi){ + + // prepare subspace polynomials + const auto layersVanishingPoly = bigPoly.addSubspacePoly(spacesGenerator.getNetworkLayersBasis()); + + // calculate the shift for last network column + const size_t lastColIndex = commonDef.imageWidth() - 2; + const FieldElement lastColShift = instanceMapping.mapPermutationColumnId_spaceElement(lastColIndex); + + + /****************************************************************** + * add the routing bits checks (verify they are all boolean) + ******************************************************************/ + { + const size_t routingBitsLayerIndex0 = 2*commonDef.variablesPerm().size(); + const size_t routingBitsLayerIndex1 = routingBitsLayerIndex0 + 1; + const FieldElement routingBitsLayerOffset0 = instanceMapping.mapPermutationLayerId_spaceElement(routingBitsLayerIndex0); + const FieldElement routingBitsLayerOffset1 = instanceMapping.mapPermutationLayerId_spaceElement(routingBitsLayerIndex1); + + // construct the auxiliary polynomials, + // those the polynomials that vanishes on both routing bits layers, + // on the zero column (no network there) + // and on the last column (no routing bits there) + const FieldElement layer0_zeroColShift = instanceMapping.mapPermutationLayerId_spaceElement(routingBitsLayerIndex0); + const FieldElement layer1_zeroColShift = instanceMapping.mapPermutationLayerId_spaceElement(routingBitsLayerIndex1); + const FieldElement layer0_lastColShift = lastColShift + layer0_zeroColShift; + const FieldElement layer1_lastColShift = lastColShift + layer1_zeroColShift; + + const AcspSummandsPolynomial::AuxPoly layer0_zeroCol_poly(columnVanishingPoly,layer0_zeroColShift); + const AcspSummandsPolynomial::AuxPoly layer1_zeroCol_poly(columnVanishingPoly,layer1_zeroColShift); + const AcspSummandsPolynomial::AuxPoly layer0_lastCol_poly(columnVanishingPoly,layer0_lastColShift); + const AcspSummandsPolynomial::AuxPoly layer1_lastCol_poly(columnVanishingPoly,layer1_lastColShift); + + const AcspSummandsPolynomial::AuxPolyVec auxPolys0 = {layer0_zeroCol_poly, layer0_lastCol_poly}; + const AcspSummandsPolynomial::AuxPolyVec auxPolys1 = {layer1_zeroCol_poly, layer1_lastCol_poly}; + + //construct the booleanity test polynomial + const booleanityTestPoly test0(neighbors.locationOfId(routingBitsLayerIndex0) + 1); + const booleanityTestPoly test1(neighbors.locationOfId(routingBitsLayerIndex1) + 1); + + // add the summand + bigPoly.addSummand(layersVanishingPoly, routingBitsLayerOffset0, test0, auxPolys0); + bigPoly.addSummand(layersVanishingPoly, routingBitsLayerOffset1, test1, auxPolys1); + } + + /****************************************************************** + * add the last DeBruijn column identicle check + ******************************************************************/ + for(size_t currVar=0; currVar < commonDef.variablesPerm().size(); currVar++){ + const size_t currLayer = 2*currVar; + + // get the test shift + const FieldElement testShift = lastColShift + instanceMapping.mapPermutationLayerId_spaceElement(currLayer); + + // get the constraint polynomial + const equivalenceTestPoly test(1+neighbors.locationOfId(currLayer), 1+neighbors.locationOfTwinLayer(currLayer)); + + // add the test + bigPoly.addSummand(columnVanishingPoly, testShift, test, {}); + } + + /****************************************************************** + * add the zero element in routing table + * is routed to itself + * and its value is the padding value check + ******************************************************************/ + for(size_t currVar=0; currVar < 2*commonDef.variablesPerm().size(); currVar++){ + const size_t currLayer = currVar; + + // get the test shift + const FieldElement testShift = instanceMapping.mapPermutationElement_spaceElement(0,currLayer); + + // get the expected value + const FieldElement expectedVal = paddingPi[commonDef.variablesPerm()[currVar>>1]]; + + // get the constraint polynomial + const constValTestPoly test(1+neighbors.locationOfId(currVar), expectedVal); + + // add the test + bigPoly.addSummand(idPoly, testShift, test, {}); + } + + /****************************************************************** + * embed the DeBruijn routing checks + ******************************************************************/ + { + //a polynomial that vanishes on the vector space where DeBruijn vertexes + //have only 2 neighbors each, namely the most significant bits of + //both the column ID and the row ID are zeros. + // + //Notice this is a subspace of the layer space, and it contains as + //well the last column of the DeBruijn, which has no outgoing + //edges from it vertexes at all + const auto vanishingOnDeBruijnTwoNeighborsSpace = bigPoly.addSubspacePoly(spacesGenerator.getDeBruijnNeighborsPairSpaceBasis()); + + + //calculate shifts for DeBruijn routing test + const vector DeBruijnShifts = spacesGenerator.getDeBruijnLayerCosets(); + + //each routed variable uses two DeBruijn layers + const size_t amountOfLayersToCheck = commonDef.variablesPerm().size()*2; + + for(size_t currLayer=0; currLayer < amountOfLayersToCheck; currLayer++){ + + // Construct the auxiliary polynomials + const FieldElement currLayerOffset = instanceMapping.mapPermutationLayerId_spaceElement(currLayer); + const AcspSummandsPolynomial::AuxPoly vanishingOnZeroColumn(columnVanishingPoly,currLayerOffset); + const AcspSummandsPolynomial::AuxPoly vanishingOnLastColumn(columnVanishingPoly,currLayerOffset + lastColShift); + const AcspSummandsPolynomial::AuxPolyVec auxPolys = {vanishingOnZeroColumn, vanishingOnLastColumn}; + + for(size_t cosetId=0; cosetId < DeBruijnShifts.size(); cosetId++){ + + // Construct the test polynomial + const size_t currVal_var = neighbors.locationOfId(currLayer) + 1; + const size_t N0_var = neighbors.locationOfDeBruijn(0,cosetId,currLayer) + 1; + const size_t N1_var = neighbors.locationOfDeBruijn(1,cosetId,currLayer) + 1; + const size_t bit_var = neighbors.locationOfRoutingBit(currLayer) + 1; + const DeBruijnRoutingTestPoly test(currVal_var, N0_var, N1_var, bit_var); + + // Add the test + bigPoly.addSummand(vanishingOnDeBruijnTwoNeighborsSpace, DeBruijnShifts[cosetId] + currLayerOffset, test, auxPolys); + } + } + } +} + +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/constraints.hpp b/libstark/src/reductions/BairToAcsp/Details/constraints.hpp new file mode 100644 index 0000000..fc13e2e --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/constraints.hpp @@ -0,0 +1,26 @@ +#ifndef BairToAcsp_CONSTRAINTS_HPP__ +#define BairToAcsp_CONSTRAINTS_HPP__ + +#include "neighborsConstructor.hpp" +#include "spaces.hpp" +#include "common.hpp" +#include "instanceMappings.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +class constraints{ +public: + static std::unique_ptr getConstraintsPoly( +const BairInstance& instance, +const AcspNeighbors& neighbors, +const common& commonDef, +const instanceMappings& instanceMapping, +const spaces& spacesGenerator, +const CS_testLocations& testLocations); +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //#ifndef BairToAcsp_CONSTRAINTS_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/instanceMappings.cpp b/libstark/src/reductions/BairToAcsp/Details/instanceMappings.cpp new file mode 100644 index 0000000..2322a04 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/instanceMappings.cpp @@ -0,0 +1,69 @@ +#include "instanceMappings.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +using Algebra::FieldElement; + +instanceMappings::instanceMappings(const common& commonInfo): + commonMappings(commonInfo), + commonInfo_(commonInfo){}; + +FieldElement instanceMappings::mapVariable(const size_t varId)const{ + const auto location = commonInfo_.getVarLocation(varId); + FieldElement res; + + if (location.inPerm){ + res = mapPermutationElement_spaceElement(0,2*location.index); + } + else{ + res = mapNonPermutationVariable_spaceElement(location.index); + } + + return res; +} + +commonMappings::witnessElement_t instanceMappings::mapVariable_witnessElement(const size_t varId)const{ + const auto location = commonInfo_.getVarLocation(varId); + witnessElement_t res; + + if (location.inPerm){ + res = mapPermutationElement_witnessElement(0,2*location.index); + } + else{ + res = mapNonPermutationVariable_witnessElement(location.index); + } + + return res; +} + +FieldElement instanceMappings::mapNonPermutation_zeroRow(const size_t elementId)const{ + return mapNonPermutationElement(elementId); +} + +FieldElement instanceMappings::mapNonPermutation_lastRow(const size_t elementId)const{ + return mapNonPermutationElement (elementId) + getLastRowIndicator(); +} + +FieldElement instanceMappings::getLastRowIndicator()const{ + /// + /// Let \f$ p \f$ be the modulus polynomial used for row Id embedding. + /// Let \f$ x \f$ be the generator, so the last row id is mapped to \f$ \alpha = x^{-1} \f$, + /// in particular, \f$ x \cdot \alpha \equiv 1 ( \mod p ) \f$ + /// (\f$ \alpha \f$ is a polynomial with degree strictly less than \f$ \deg p\f$). + /// We conclude that \f$ x \cdot \alpha = p + 1 \f$, + /// hence \f$ \alpha = \frac{p+1}{x} \f$. + /// We notice \f$ p(0) \ne 0 \f$ because it is irreducible, + /// (it is required for the above to be defined, of course), + /// So we conclude that if \f$ p = \sum_{i=0}^d \beta_i x^i \f$ than + /// \f$ \alpha = \sum_{i=0}^{d-1} \beta_{i+1} x^i \f$. + /// This is exactly the way we compute \f$ \alpha = x^{-1} \f$ + /// + + const int64_t irred = commonInfo_.rowsModulus(); + const int64_t res = irred>>1; + return Algebra::mapIntegerToFieldElement(0,Algebra::ExtensionDegree,res); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/instanceMappings.hpp b/libstark/src/reductions/BairToAcsp/Details/instanceMappings.hpp new file mode 100644 index 0000000..4e8fd0b --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/instanceMappings.hpp @@ -0,0 +1,27 @@ +#ifndef _COMMON_BairToAcsp_INSTANCEMAPPINGS_HPP__ +#define _COMMON_BairToAcsp_INSTANCEMAPPINGS_HPP__ + +#include "commonMappings.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +class instanceMappings : public commonMappings{ +public: + instanceMappings(const common& commonInfo); + Algebra::FieldElement mapVariable(const size_t varId)const; + witnessElement_t mapVariable_witnessElement(const size_t varId)const; + Algebra::FieldElement mapNonPermutation_zeroRow(const size_t elementId)const; + Algebra::FieldElement mapNonPermutation_lastRow(const size_t elementId)const; +protected: + const common& commonInfo_; + +private: + Algebra::FieldElement getLastRowIndicator()const; +}; + +} //namespace BairToAcsp +} //namespace libstark + + +#endif // #ifndef _COMMON_BairToAcsp_INSTANCEMAPPINGS_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/neighborsConstructor.cpp b/libstark/src/reductions/BairToAcsp/Details/neighborsConstructor.cpp new file mode 100644 index 0000000..f774dfe --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/neighborsConstructor.cpp @@ -0,0 +1,401 @@ +#include "neighborsConstructor.hpp" + +#include + +namespace libstark{ +namespace BairToAcsp{ + +using Algebra::LinearPolynomial; +using Algebra::PolynomialInterface; +using Algebra::UnivariatePolynomialInterface; +using Algebra::zero; +using Algebra::one; +using Algebra::FieldElement; +using Algebra::mapIntegerToFieldElement; + +using std::find; +using std::array; +using std::vector; +using std::map; +using std::unique_ptr; +using std::move; + +typedef AcspNeighbors::polynomialIndicator_t polynomialIndicator_t; + +AcspNeighbors::AcspNeighbors(const BairInstance& bairInstance ,const common& commonDef, const instanceMappings& instanceMapping, const CS_testLocations& testLocations): +commonDef_(commonDef), +instanceMapping_(instanceMapping), +testLocations_(testLocations), +permutationCS_usageVector_(getCsUsageVector(commonDef.getConstraintsPi(),bairInstance.constraintsPermutation().numVars())), +assignmentCS_usageVector_(getCsUsageVector(commonDef.getConstraintsChi(),bairInstance.constraintsAssignment().numVars())), +neighbors_(commonDef.witnessLayersAmount()) +{ + //Construct the Permutation CS neighbors + constructPermCS(); + + //Construct the Permutation CS neighbors + constructAssignmentCS(); + + // + // Routing network related naighbors + // + + if (commonDef.hasRoutingNetwork()){ + initRoutingNetworkNeighbors(); + } + + //Init the offsets + neighbors_witnessOffsets_ = initOffsets(neighbors_); +} + +void AcspNeighbors::initRoutingNetworkNeighbors(){ + + //Add the Identity neighbor + constructId(); + + //Add the TWIN LAYER neighbor + constructTwinLayer(); + + //Add the DeBruijn network neighbors + constructDeBruijn(); + + //Add neighbors for accessing the routing bit from any layer of the network + constructRoutingBitAccess(); +} + +vector > > AcspNeighbors::getNeighborPolynomials()const{ + vector > > res; + + for(const auto& nVec : neighbors_){ + res.resize(res.size()+1); + auto& currRes = res[res.size()-1]; + for(const LinearPolynomial& neighbor : nVec){ + currRes.push_back(unique_ptr(new LinearPolynomial(neighbor))); + } + } + + return move(res); +} + +size_t AcspNeighbors::polynomialsNum()const{ + return neighbors_witnessOffsets_[neighbors_.size()-1] + neighbors_[neighbors_.size()-1].size(); +} + +size_t AcspNeighbors::locationOfId(const size_t layerId)const{ + return retLocation(locationOfId_[layerId]); +} + +size_t AcspNeighbors::locationOfTwinLayer(const size_t layerId)const{ + return retLocation(locationOfTwinLayer_[layerId]); +} + +size_t AcspNeighbors::locationOfDeBruijn(const short dbNeighborId, const short affineCosetId ,const size_t layerId)const{ + return retLocation(locationOfDeBruijn_[dbNeighborId][affineCosetId][layerId]); +} + +size_t AcspNeighbors::locationOfRoutingBit(const size_t layerId)const{ + return retLocation(locationOfRoutingBit_[layerId]); +} + +size_t AcspNeighbors::locationOfPermCS(polynomialIndicator_t poly, const size_t varId)const{ + return retLocation(locationOfPermCS_.at(poly)[varId]); +} + +bool AcspNeighbors::existsPermCS(polynomialIndicator_t poly, const size_t varId)const{ + return locationOfPermCS_.at(poly)[varId].exists; +} + +size_t AcspNeighbors::locationOfAssignmentCS(polynomialIndicator_t poly, const size_t varId, const size_t neighborVersion)const{ + _COMMON_ASSERT((neighborVersion == 0)||(neighborVersion == 1),"neighbor version must be 0 or 1"); + const size_t res = retLocation(locationOfAssignmentCS_.at(poly)[varId][neighborVersion]); + return res; +} + +bool AcspNeighbors::existsAssignmentCS(polynomialIndicator_t poly, const size_t varId)const{ + return locationOfAssignmentCS_.at(poly)[varId][0].exists; +} + +vector AcspNeighbors::initOffsets(const vector>& src){ + vector res; + size_t currOffset = 0; + for(const auto& v : src){ + res.push_back(currOffset); + currOffset += v.size(); + } + + return res; +} + +struct AcspNeighbors::NeighborLocation_stc AcspNeighbors::addNeighbor(const struct PolyAndLayer_stc& neighbor){ + + //check if this neighbor already exists, + //if so, return its index + auto& layerNeighbors = neighbors_[neighbor.layer]; + const auto currLocation = find(layerNeighbors.begin(), layerNeighbors.end(), neighbor.poly); + if(currLocation != layerNeighbors.end()){ + return NeighborLocation_stc(neighbor.layer, currLocation - layerNeighbors.begin()); + } + + //otherwise, add it to the vector + //and return its index + layerNeighbors.push_back(neighbor.poly); + return NeighborLocation_stc(neighbor.layer, layerNeighbors.size() - 1); +} + +Algebra::FieldElement AcspNeighbors::getGenerator()const{ + return Algebra::xFE(); +} + +size_t AcspNeighbors::retLocation(const struct NeighborLocation_stc& loc)const{ + if(!loc.exists) _COMMON_FATAL("Neighbor does not exist"); + return loc.index + neighbors_witnessOffsets_[loc.layer]; +} + +LinearPolynomial AcspNeighbors::constructIdNeighbor(){ + return LinearPolynomial(zero(), one()); +} + +LinearPolynomial AcspNeighbors::constructTwinLayerNeighbor()const{ + const FieldElement STEP = instanceMapping_.mapPermutationLayerId_spaceElement(1)-instanceMapping_.mapPermutationLayerId_spaceElement(0); + return LinearPolynomial(STEP,one()); +} + +LinearPolynomial AcspNeighbors::constructLinearDeBruijn_N0()const{ + const FieldElement generator = getGenerator(); + return LinearPolynomial(zero(),generator); +} + +LinearPolynomial AcspNeighbors::constructLinearDeBruijn_N1()const{ + const LinearPolynomial bitXor(one(),one()); + const LinearPolynomial N0 = constructLinearDeBruijn_N0(); + + //because we use reverse DeBruijn network, the bit xor + //should be done before moving to "Neighbor 0" + return N0.compose(bitXor); +} + +LinearPolynomial AcspNeighbors::applyShiftedLinearOperation(const size_t layer_id, const LinearPolynomial operation)const{ + const LinearPolynomial moveToLayer(instanceMapping_.mapPermutationLayerId_spaceElement(layer_id),one()); + return operation.compose(moveToLayer); +} + +FieldElement AcspNeighbors::DeBruijn_fixRowIdCarry()const{ + const size_t numBits = commonDef_.heightSpaceDimension(); + const size_t shift = 0; + size_t fixAsInt = 1 + (1<> AcspNeighbors::getCsUsageVector(const vector>& cs,const size_t numVars){ + map> usageVector; + + for(size_t poly_indx=0; poly_indx < cs.size(); poly_indx++){ + vector currPolyUsage; + for(size_t varId=0; varId< numVars; varId++){ + if(cs[poly_indx]->isEffectiveInput(varId)){ + currPolyUsage.push_back(true); + } + else{ + currPolyUsage.push_back(false); + } + } + + //add curr poly usage vector to CS usage vector + usageVector.emplace(poly_indx,currPolyUsage); + } + + return usageVector; +} + +LinearPolynomial AcspNeighbors::moveFromPointToVarId(const FieldElement src, const size_t varId)const{ + const FieldElement destination = instanceMapping_.mapVariable(varId); + + const FieldElement delta = destination - src; + + return LinearPolynomial(delta,one()); +} + +AcspNeighbors::PolyAndLayer_stc AcspNeighbors::moveFromPointToVarId_withLayer(const FieldElement src, const size_t varId)const{ + const auto destination = instanceMapping_.mapVariable_witnessElement(varId); + + const FieldElement delta = destination.second - src; + + return PolyAndLayer_stc(LinearPolynomial(delta,one()), destination.first); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/neighborsConstructor.hpp b/libstark/src/reductions/BairToAcsp/Details/neighborsConstructor.hpp new file mode 100644 index 0000000..2d36190 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/neighborsConstructor.hpp @@ -0,0 +1,144 @@ +#ifndef NEIGHBORS_CONSTRUCTOR_HPP__ +#define NEIGHBORS_CONSTRUCTOR_HPP__ + +#include "common.hpp" +#include "instanceMappings.hpp" +#include "constraintSystemsTestLocations.hpp" +#include + +#include +#include +#include +#include + +namespace libstark{ +namespace BairToAcsp{ + +class AcspNeighbors{ +public: + typedef size_t polynomialIndicator_t; + + AcspNeighbors(const BairInstance& bairInstance,const common& commonDef, const instanceMappings& instanceMapping, const CS_testLocations& testLocations); + std::vector > > getNeighborPolynomials()const; + size_t polynomialsNum()const; + + size_t locationOfId(const size_t layerId)const; + size_t locationOfTwinLayer(const size_t layerId)const; + size_t locationOfDeBruijn(const short dbNeighborId, const short affineCosetId ,const size_t layerId)const; + size_t locationOfRoutingBit(const size_t layerId)const; + size_t locationOfPermCS(polynomialIndicator_t poly, const size_t varId)const; + bool existsPermCS(polynomialIndicator_t poly, const size_t varId)const; + size_t locationOfAssignmentCS(polynomialIndicator_t poly, const size_t varId, const size_t neighborVersion)const; + bool existsAssignmentCS(polynomialIndicator_t poly, const size_t varId)const; + + +private: + + struct NeighborLocation_stc{ + bool exists; + size_t layer; + size_t index; + + NeighborLocation_stc(): exists(false), layer(0), index(0){}; + NeighborLocation_stc(size_t index_): exists(true), layer(0), index(index_){}; + NeighborLocation_stc(size_t layer_, size_t index_): exists(true), layer(layer_), index(index_){}; + }; + + struct PolyAndLayer_stc{ + Algebra::LinearPolynomial poly; + size_t layer; + + PolyAndLayer_stc(const Algebra::LinearPolynomial& poly_):poly(poly_), layer(0){}; + PolyAndLayer_stc(const Algebra::LinearPolynomial& poly_, size_t layer_):poly(poly_), layer(layer_){}; + PolyAndLayer_stc compose(const Algebra::LinearPolynomial& p_)const{ + return PolyAndLayer_stc(poly.compose(p_),layer); + } + }; + + //reduction common + const common& commonDef_; + const instanceMappings instanceMapping_; + const CS_testLocations testLocations_; + + //usage map of constraint systems polynomials + const std::map< polynomialIndicator_t, std::vector > permutationCS_usageVector_; + const std::map< polynomialIndicator_t, std::vector > assignmentCS_usageVector_; + + // + //neighbor polynomials + // + + std::vector> neighbors_; + std::vector neighbors_witnessOffsets_; + + + /****************************************** + * Neighbors locations + ******************************************/ + + // + //locations for common neighbors + // + + // Permutation constraint system neighbors + std::map> locationOfPermCS_; + + // Assignment constraint system neighbors + std::map>> locationOfAssignmentCS_; + + // + //locations for routing network neighbors + // + + // ID polynomial (aka x) + std::vector locationOfId_; + + // Twin network layer access + std::vector locationOfTwinLayer_; + + //accessed using the index [layer ID][DeBruijn neighbor][coset ID] + std::vector locationOfDeBruijn_[2][4]; + + //accessed using the layer ID + std::vector locationOfRoutingBit_; + + /******************************************** + * private methods + *********************************************/ + + static std::vector initOffsets(const std::vector>& src); + struct NeighborLocation_stc addNeighbor(const struct PolyAndLayer_stc& neighbor); + size_t retLocation(const struct NeighborLocation_stc& loc)const; + void initRoutingNetworkNeighbors(); + Algebra::FieldElement getGenerator()const; + static std::map> getCsUsageVector(const std::vector>& cs, size_t varsAmount); + + //neighbors constructors + static Algebra::LinearPolynomial constructIdNeighbor(); + Algebra::LinearPolynomial constructTwinLayerNeighbor()const; + void constructId(); + void constructTwinLayer(); + void constructDeBruijn(); + void constructRoutingBitAccess(); + void constructPermCS(); + void constructAssignmentCS(); + + //neighbors constructor helper functions + Algebra::LinearPolynomial applyShiftedLinearOperation(const size_t layer_id, const Algebra::LinearPolynomial operation)const; + Algebra::LinearPolynomial constructLinearDeBruijn_N0()const; + Algebra::LinearPolynomial constructLinearDeBruijn_N1()const; + Algebra::FieldElement DeBruijn_fixRowIdCarry()const; + Algebra::FieldElement DeBruijn_fixColumnIdCarry()const; + Algebra::FieldElement DeBruijn_getFixByCoset(const short cosetId)const; + PolyAndLayer_stc constructPermCS(const size_t nonPermElemId, const size_t varId)const; + PolyAndLayer_stc constructAssignmentCS(const size_t nonPermElemId, const size_t varId, const bool withCarry)const; + Algebra::LinearPolynomial moveFromPointToVarId(const Algebra::FieldElement src, const size_t varId)const; + PolyAndLayer_stc moveFromPointToVarId_withLayer(const Algebra::FieldElement src, const size_t varId)const; +}; + + +} //namespace BairToAcsp +} //namespace libstark + + +#endif //#ifdef NEIGHBORS_CONSTRUCTOR_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/spaces.cpp b/libstark/src/reductions/BairToAcsp/Details/spaces.cpp new file mode 100644 index 0000000..f3f7926 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/spaces.cpp @@ -0,0 +1,160 @@ +#include "spaces.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +using Algebra::FieldElement; +using Algebra::zero; +using Algebra::elementsSet_t; +using Algebra::mapIntegerToFieldElement; +using Infrastructure::POW2; +using std::vector; + +size_t spaces::offsetOfNetworkLayerId()const{ + return commonDef_.heightSpaceDimension() + commonDef_.widthSpaceDimension(); +} + +elementsSet_t spaces::getVanishingSpaceBasis()const{ + elementsSet_t basis; + + const size_t amountOfBasisElement = commonDef_.vanishingSpaceDimension(); + + for(size_t i=0; i< amountOfBasisElement; i++){ + //the i'th element of the standard basis + FieldElement basisElement = mapIntegerToFieldElement(i,1,1); + + //add to basis + basis.insert(basisElement); + } + + return basis; +} + +elementsSet_t spaces::getNetworkLayersBasis()const{ + elementsSet_t basis; + + //construct the standard basis for the two layers space + const size_t amountOfBasisElement = offsetOfNetworkLayerId(); + + for(size_t i=0; i< amountOfBasisElement; i++){ + //the i'th element of the standard basis + FieldElement basisElement = mapIntegerToFieldElement(i,1,1); + + //add to basis + basis.insert(basisElement); + } + + return basis; +} + +//returns the basis of a column space +//as a subspace of a layer +elementsSet_t spaces::getNetworkColumnBasis()const{ + elementsSet_t basis; + + //construct the standard basis for the column linear space + const size_t amountOfBasisElement = commonDef_.heightSpaceDimension(); + const size_t firstStandardBasisElement = 0; + for(size_t i=0; i< amountOfBasisElement; i++){ + //the i'th element of the standard basis + FieldElement basisElement = mapIntegerToFieldElement(firstStandardBasisElement+i,1,1); + + //add to basis + basis.insert(basisElement); + } + + return basis; +} + +//returns the basis of half a column space +//as a subspace of a layer +//It spans only the parts where the vectorId has a 0 most significant bit +elementsSet_t spaces::getNetworkColumnAssignmentNoCarryBasis()const{ + elementsSet_t basis; + + //construct the standard basis for the half column linear space + const size_t amountOfBasisElement = commonDef_.heightSpaceDimension(); + const size_t firstStandardBasisElement = 0; + for(size_t i=0; i< amountOfBasisElement-1; i++){ + //the i'th element of the standard basis + FieldElement basisElement = mapIntegerToFieldElement(firstStandardBasisElement+i,1,1); + + //add to basis + basis.insert(basisElement); + } + + return basis; +} + +//indexes that are not included in a basis for DeBruijn routing checks +vector spaces::getBitIndexesForDeBruijnNeighboursPairIndicator()const{ + + const size_t amountOfIndicatorBits = 2; + const size_t colId_lastBitLocation = commonDef_.heightSpaceDimension() + commonDef_.widthSpaceDimension() - 1; + const size_t rowId_lastBitLocation = commonDef_.heightSpaceDimension() - 1; + + + vector bitsIndicator(amountOfIndicatorBits); + bitsIndicator[0] = colId_lastBitLocation; + bitsIndicator[1] = rowId_lastBitLocation; + + return bitsIndicator; +} + +//Returns the basis of a subspace of a layer space +//Such that all the vertexes in the DeBruijn network +//in that space has the same 2 neighbours +elementsSet_t spaces::getDeBruijnNeighborsPairSpaceBasis()const{ + elementsSet_t basis; + + //construct the subset of the layers basis that does not include + //the basis elements relevant for the neighbor pairs indicator + const size_t amountOfLayerBasisElement = offsetOfNetworkLayerId(); + + //'i's that should not be included + vector notIncludedIndexes = getBitIndexesForDeBruijnNeighboursPairIndicator(); + + for(size_t i=0; i< amountOfLayerBasisElement; i++){ + + //check if 'i' should be not included in the basis + if(count(notIncludedIndexes.begin(),notIncludedIndexes.end(),i) > 0)continue; + + //the i'th element of the standard basis + FieldElement basisElement = mapIntegerToFieldElement(i,1,1); + + //add to basis + basis.insert(basisElement); + } + + return basis; +} + +//calculate shifts for DeBruijn routing test +vector spaces::getDeBruijnLayerCosets()const{ + const vector cosetIndicatorBits = getBitIndexesForDeBruijnNeighboursPairIndicator(); + const size_t amountOfCosets = POW2(cosetIndicatorBits.size()); + vector DeBruijnShifts(amountOfCosets); + for(size_t cosetId=0; cosetId < amountOfCosets; cosetId++){ + + //construct an element in the coset + FieldElement cosetRepresentor = zero(); + { + size_t currCosetId = cosetId; + for(const size_t bitIndex : cosetIndicatorBits){ + size_t currBitVal = currCosetId&1; + if(currBitVal == 1){ + cosetRepresentor += mapIntegerToFieldElement(bitIndex,1,1); + } + currCosetId>>=1; + } + + } + DeBruijnShifts[cosetId] = cosetRepresentor; + } + + return DeBruijnShifts; +} + + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/spaces.hpp b/libstark/src/reductions/BairToAcsp/Details/spaces.hpp new file mode 100644 index 0000000..8807afa --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/spaces.hpp @@ -0,0 +1,46 @@ +#ifndef BairToAcsp_SPACES_HPP__ +#define BairToAcsp_SPACES_HPP__ + +#include "common.hpp" + +#include + +namespace libstark{ +namespace BairToAcsp{ + +class spaces{ +public: + spaces(const common& commonDef):commonDef_(commonDef){}; + + //returns the basis of the vanishing space + Algebra::elementsSet_t getVanishingSpaceBasis()const; + + //returns the basis of a pair of network layers (the basis of the linear space of the first two layers) + Algebra::elementsSet_t getNetworkLayersBasis()const; + + //returns the basis of a column space + Algebra::elementsSet_t getNetworkColumnBasis()const; + + //returns the basis of half a column space + //it contains on the parts of the columns where the vectorId + //has a 0 value in it most significant bit + Algebra::elementsSet_t getNetworkColumnAssignmentNoCarryBasis()const; + + //Returns the basis of a subspace of a layer space + //Such that all the vertexes in the DeBruijn network + //in that space has the same 2 neighbours + Algebra::elementsSet_t getDeBruijnNeighborsPairSpaceBasis()const; + + //calculate shifts for DeBruijn routing test + std::vector getDeBruijnLayerCosets()const; +private: + const common& commonDef_; + + size_t offsetOfNetworkLayerId()const; + std::vector getBitIndexesForDeBruijnNeighboursPairIndicator()const; +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif // #ifndef BairToAcsp_SPACES_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/witnessMappings.cpp b/libstark/src/reductions/BairToAcsp/Details/witnessMappings.cpp new file mode 100644 index 0000000..cd9609a --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/witnessMappings.cpp @@ -0,0 +1,84 @@ +#include "witnessMappings.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +using Algebra::FieldElement; +using Algebra::mapIntegerToFieldElement; +using Algebra::mapFieldElementToInteger; +using Algebra::elementsSet_t; +using Infrastructure::Log2; +using std::vector; + +witnessMappings::witnessMappings(const common& commonInfo): + commonMappings(commonInfo), + firstRoutingBitsLayer_(2*commonInfo.variablesPerm().size()), + overflow_mask_(commonInfo.imageHeight()), + imageSpaceDim_(calculateImageSpaceDim(commonInfo)){}; + +FieldElement witnessMappings::mapNonPermutationElement(const size_t vecId, const size_t varIndex)const{ + return map_x_power_modulu_poly(vecId, rowsModulus_) + commonMappings::mapNonPermutationElement(varIndex); +} + +witnessMappings::witnessElement_t witnessMappings::mapNonPermutationElement_witness(const size_t vecId, const size_t varIndex)const{ + witnessElement_t res = commonMappings::mapNonPermutationElement_witness(varIndex); + res.second += map_x_power_modulu_poly(vecId, rowsModulus_); + + return res; +} + +witnessMappings::spaceIndex_t witnessMappings::getNextRow_spaceIndex(const spaceIndex_t& row_spaceIndex)const{ + spaceIndex_t nextVal = row_spaceIndex<<1; + if(nextVal & overflow_mask_){ + nextVal ^= rowsModulus_; + } + return nextVal; +} + +witnessMappings::spaceIndex_t witnessMappings::mapIndexOfNonPermutationVariable_spaceIndex(const spaceIndex_t& row_spaceIndex, const size_t& varIndex)const{ + return row_spaceIndex ^ mapNonPermutationVariable_spaceIndex(varIndex); +} + +witnessMappings::witnessIndex_t witnessMappings::mapIndexOfNonPermutationVariable_witnessIndex(const spaceIndex_t& row_spaceIndex, const size_t& varIndex)const{ + const auto spaceIdx = mapIndexOfNonPermutationVariable_spaceIndex(row_spaceIndex, varIndex); + return map_spaceIndex_to_witnessIndex(spaceIdx); +} + +witnessMappings::spaceIndex_t witnessMappings::mapNetworkElement_spaceIndex(const size_t rowId, const size_t column, const size_t layerId)const{ + return rowId ^ mapPermutationElement_spaceIndex(column,layerId); +} + +witnessMappings::witnessIndex_t witnessMappings::mapNetworkElement_witnessIndex(const size_t rowId, const size_t column, const size_t layerId)const{ + const auto spaceIdx = mapNetworkElement_spaceIndex(rowId,column,layerId); + return map_spaceIndex_to_witnessIndex(spaceIdx); +} + +witnessMappings::spaceIndex_t witnessMappings::mapNetworkRoutingBit_spaceIndex(const size_t rowId, const size_t column, const size_t layerId)const{ + const size_t routingBitslLayer = (layerId%2) + firstRoutingBitsLayer_; + return mapNetworkElement_spaceIndex(rowId,column,routingBitslLayer); +} + +witnessMappings::witnessIndex_t witnessMappings::mapNetworkRoutingBit_witnessIndex(const size_t rowId, const size_t column, const size_t layerId)const{ + const auto spaceIdx = mapNetworkRoutingBit_spaceIndex(rowId,column,layerId); + return map_spaceIndex_to_witnessIndex(spaceIdx); +} + +vector witnessMappings::getImageSpaceOrderedBasis()const{ + vector imageSpaceBasis; + for(size_t i=0; i< imageSpaceDim_; i++){ + //the i'th element of the standard basis + FieldElement basisElement = mapIntegerToFieldElement(i,1,1); + + //add to basis + imageSpaceBasis.push_back(basisElement); + } + return imageSpaceBasis; +} + +size_t witnessMappings::calculateImageSpaceDim(const commonDeffinitions& commonDef){ + const size_t dimOfImage = commonDef.widthSpaceDimension() + commonDef.heightSpaceDimension(); + return dimOfImage + (SUPPORT_ZK?1:0); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/witnessMappings.hpp b/libstark/src/reductions/BairToAcsp/Details/witnessMappings.hpp new file mode 100644 index 0000000..3fea638 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/witnessMappings.hpp @@ -0,0 +1,45 @@ +#ifndef Bair_TO_Acsp_WITNESS_MAPPING_HPP__ +#define Bair_TO_Acsp_WITNESS_MAPPING_HPP__ + +#include "commonMappings.hpp" +#include + +namespace libstark{ +namespace BairToAcsp{ + +class witnessMappings : public commonMappings{ +public: + witnessMappings(const common& commonInfo); + Algebra::FieldElement mapNonPermutationElement(const size_t vecId, const size_t varIndex)const; + witnessElement_t mapNonPermutationElement_witness(const size_t vecId, const size_t varIndex)const; + + /// + /// Let \f$ \eta \f$ be the rows modulus, of degree \f$ d \f$ + /// This function maps the elementId \f$ \sum_{i=0}^{n} b_i 2^i \f$ + /// to the field element \f$ b_0 \cdot \eta + x^d \cdot \sum_{i=1}^{n} b_i x^i \f$ + /// + /// For more details ask Michael Riabzev (this method reduces maximal number of neighbors) + /// + spaceIndex_t mapIndexOfNonPermutationVariable_spaceIndex(const spaceIndex_t& row_spaceIndex, const size_t& varIndex)const; + witnessIndex_t mapIndexOfNonPermutationVariable_witnessIndex(const spaceIndex_t& row_spaceIndex, const size_t& varIndex)const; + spaceIndex_t getFirstRow_spaceIndex()const{return 1;} + spaceIndex_t getNextRow_spaceIndex(const spaceIndex_t& row_spaceIndex)const; + + spaceIndex_t mapNetworkElement_spaceIndex(const size_t rowId, const size_t column, const size_t layerId)const; + witnessIndex_t mapNetworkElement_witnessIndex(const size_t rowId, const size_t column, const size_t layerId)const; + spaceIndex_t mapNetworkRoutingBit_spaceIndex(const size_t rowId, const size_t column, const size_t layerId)const; + witnessIndex_t mapNetworkRoutingBit_witnessIndex(const size_t rowId, const size_t column, const size_t layerId)const; + + std::vector getImageSpaceOrderedBasis()const; +private: + const size_t firstRoutingBitsLayer_; + const spaceIndex_t overflow_mask_; + const size_t imageSpaceDim_; + + static size_t calculateImageSpaceDim(const commonDeffinitions& commonDef); +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //Bair_TO_Acsp_WITNESS_MAPPING_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Details/witnessReduction.cpp b/libstark/src/reductions/BairToAcsp/Details/witnessReduction.cpp new file mode 100644 index 0000000..2e0e1b3 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/witnessReduction.cpp @@ -0,0 +1,264 @@ +#include "witnessReduction.hpp" +#include "../Routing/LongSymmetricDeBruijnNetwork.hpp" +#include "common/Utils/TaskReporting.hpp" + +#include + +namespace libstark{ +namespace BairToAcsp{ + +using std::vector; +using std::unique_ptr; +using Algebra::UnivariatePolynomialGeneral; +using Algebra::FieldElement; +using Algebra::zero; +using Algebra::one; + +typedef LongSymmetricDeBruijnNetwork permNetwork_t; + +unique_ptr witnessReduction::reduceWitness( const BairInstance& instance, const BairWitness& witness){ + + TASK("Reducing Bair witness to Acsp"); + + //get common information + common commonDef(instance); + witnessMappings witnessMapping(commonDef); + + //get the embedding + vector mappings = getEmbeddingMapping(instance, witness, commonDef,witnessMapping); + + //ordered basis for witness space + const auto& basis = witnessMapping.getImageSpaceOrderedBasis(); +//#define ZERO_WITNESS //sometimes for debugging it's convenient to use an identically zero witness +#ifdef ZERO_WITNESS + for (auto& p : mapping) + p = zero(); +#endif + //construct witness + //and return it + std::vector> assignments; + for(const auto& mapping : mappings){ + assignments.push_back(unique_ptr(new UnivariatePolynomialGeneral(mapping,basis,zero()))); + } + unique_ptr witness_ptr(new AcspWitness(move(assignments))); + return move(witness_ptr); +} + +class exponentsPermutation : public Sequence{ +public: + exponentsPermutation(const BairInstance& instance):instance_(instance),commonDef_(instance){}; + + size_t getElementByIndex(index_t index)const{ + const size_t singeltonIndex = instance_.domainSize(); + + //singleton case + if(index == singeltonIndex) return 0; + + //general case + return expModulu(index); + } + +private: + const BairInstance& instance_; + const common commonDef_; + + size_t expModulu(const size_t exp)const{ + + const int64_t modulus = commonDef_.rowsModulus(); + const size_t modulus_deg = floor(Infrastructure::Log2(modulus)); + const size_t overflow_mask = 1UL<{ + public : + inversePermutation(const Sequence& src, const size_t numElements): seq_(numElements){ + for(size_t val=0; val< numElements; val++){ + const size_t index = src.getElementByIndex(val); + seq_[index] = val; + } + } + size_t getElementByIndex(index_t index)const{ + if (index < seq_.size()) return seq_[index]; + else { + _COMMON_FATAL("Access to such index is unexpected"); + } + } + private: + vector seq_; +}; + +//represents the permutation \f$ g \circ h \circ g^{-1} \f$ +class conjugatePermutation : public Sequence{ + public: + conjugatePermutation(const Sequence& g, const Sequence& h, const size_t numElements): + g_(g), h_(h), g_inv_(g,numElements){}; + + size_t getElementByIndex(index_t index)const{ + const size_t v1 = g_inv_.getElementByIndex(index); + const size_t v2 = h_.getElementByIndex(v1); + const size_t v3 = g_.getElementByIndex(v2); + + return v3; + } + + private: + const Sequence& g_; + const Sequence& h_; + const inversePermutation g_inv_; +}; + +class addSingeltonToPermutation : public Sequence{ + public: + addSingeltonToPermutation(const Sequence& orig, const size_t singletoneIndex): origPerm_(orig) ,singletoneIndex_(singletoneIndex){}; + size_t getElementByIndex(index_t index)const{ + if (index == singletoneIndex_) return singletoneIndex_; + return origPerm_.getElementByIndex(index); + } + + private: + const Sequence& origPerm_; + const size_t singletoneIndex_; +}; + +void fillWithRandomVals(vector>& src){ + for (auto& v : src){ + for (auto& e : v){ + e = Algebra::generateRandom(); + } + } +} + +//Mapping +vector witnessReduction::getEmbeddingMapping( const BairInstance& instance, const BairWitness& witness, const common& commonDef, const witnessMappings& witnessMapping){ + + //define the mapping on which the assignment polynomial will be interpolated on + //This mapping is the arithmetization of the witness + const size_t mappingSize = Infrastructure::POW2(witnessMapping.getImageSpaceOrderedBasis().size()); + vector mappings(commonDef.witnessLayersAmount(), evaluation_t(mappingSize,Algebra::zero())); + + //fill with random values for ZK + fillWithRandomVals(mappings); + + //Map the coloring (assignment) + mapChi(instance,witness,mappings,commonDef,witnessMapping); + + //Map the routing network (Pi) if needed + if(commonDef.hasRoutingNetwork()){ + mapNetwork(instance,witness,mappings,commonDef,witnessMapping); + } + + //return the mapping + return mappings; +} + +void witnessReduction::mapChi(const BairInstance& instance, const BairWitness& witness, vector& mappings, const common& commonDef, const witnessMappings& witnessMapping){ + const size_t cyclicDomainSize = instance.domainSize(); + + const vector unroutedVars = commonDef.variablesNonPerm(); + + //ordered basis for witness space + const auto& basis = witnessMapping.getImageSpaceOrderedBasis(); + + //Map the coloring of the circle + { + auto currRow_spaceIndex = witnessMapping.getFirstRow_spaceIndex(); + for( size_t vecId =0; vecId < cyclicDomainSize; vecId++){ + const auto& assignment = witness.get_color(vecId); + for (const size_t& varId : unroutedVars){ + const size_t varIndex = commonDef.getVarLocation(varId).index; + const commonMappings::witnessIndex_t indicator = witnessMapping.mapIndexOfNonPermutationVariable_witnessIndex(currRow_spaceIndex,varIndex); + mappings[indicator.first][indicator.second] = assignment[varId]; + } + currRow_spaceIndex = witnessMapping.getNextRow_spaceIndex(currRow_spaceIndex); + } + + } + +} + +void witnessReduction::mapNetwork(const BairInstance& instance, const BairWitness& witness, vector& mappings, const common& commonDef, const witnessMappings& witnessMapping){ + + /// We want the "log" permutation, the one that maps: + /// \f$ g^i \mapsto i \f$, and for the special case: \f$ 0 \mapsto singletoneIndex \f$ + /// The log permutation defines the mapping from the label in the first column + /// of the DeBruijn to the vectorId it should hold, + /// thus it defines the mapping from the values in the network to vectors ids + const exponentsPermutation expPerm(instance); + const inversePermutation logPerm(expPerm, instance.domainSize()+1); + + //Add another element to permutation as a singleton, so we can rout it using DeBruijn + const addSingeltonToPermutation expendedPerm(witness.permutation(),instance.domainSize()); + + /// Because we mess with the indexes, we have to change the permutation as well + /// If the good permutation was \f$ i \mapsto \pi(i) \f$ + /// after changing the numbering, using some permutation \f$ \sigma \f$ + /// if the routing network will (originally) rout \f$ \pi \f$ we + /// would get the permutation \f$ \sigma(i) \mapsto \sigma \circ \pi(i) \f$ + /// But what we want is \f$ \sigma(i) \mapsto \pi \circ \sigma(i) \f$, + /// So instead we rout the conjugate permutation \f$ \sigma^{-1} \circ \pi \circ \sigma \f$ + /// This way we get: + /// \f$ \sigma(i) \mapsto \sigma \circ \sigma^{-1} \circ \pi \circ \sigma(i) = \pi \circ \sigma(i) \f$ + const conjugatePermutation permToRout(expPerm,expendedPerm, instance.domainSize()+1); + + //map routing network + permNetwork_t net(instance.domainSizeIndicator()); + net.rout(permToRout); + + const vector routedIndexes = commonDef.variablesPerm(); + + //define the index for the additional vector + const size_t addedVecId = instance.domainSize(); + + //ordered basis for witness space + const auto& basis = witnessMapping.getImageSpaceOrderedBasis(); + + //map the main routing network + { + + for (size_t rowId=0; rowId < net.height(); rowId++){ + for(size_t columnId = 0; columnId < net.getWingWidth() ; columnId++){ + for(short netPartId = 0; netPartId < 2; netPartId++){//loop over the two halves + + const RoutingNetwork::dataID_t dataId = net.getDataID(netPartId, columnId, rowId); + const size_t vecId = logPerm.getElementByIndex(dataId); + const auto& coloring = (vecId == addedVecId? instance.paddingPi() : witness.get_color(vecId)); + + //map routing network + for (size_t packetId = 0; packetId < routedIndexes.size(); packetId++){ + const auto currPacketIndex = routedIndexes[packetId]; + const FieldElement val = coloring[currPacketIndex]; + const auto indicator_index = witnessMapping.mapNetworkElement_witnessIndex(rowId,columnId,2*packetId + netPartId); + mappings[indicator_index.first][indicator_index.second] = val; + } + + //map routing bits + if( columnId < net.getWingWidth()-1){ + const short bitVal = net.routingBit(netPartId,columnId,rowId); + const auto indicator_index = witnessMapping.mapNetworkRoutingBit_witnessIndex(rowId,columnId,netPartId); + + switch(bitVal){ + case 0: mappings[indicator_index.first][indicator_index.second] = zero(); break; + case 1: mappings[indicator_index.first][indicator_index.second] = one(); break; + default : _COMMON_FATAL("Bad value of bit"); + } + } + } + } + } + + } +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Details/witnessReduction.hpp b/libstark/src/reductions/BairToAcsp/Details/witnessReduction.hpp new file mode 100644 index 0000000..c179f45 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Details/witnessReduction.hpp @@ -0,0 +1,33 @@ +#ifndef Bair_TO_Acsp_WITNESS_REDUCTION_HPP__ +#define Bair_TO_Acsp_WITNESS_REDUCTION_HPP__ + +#include "common.hpp" +#include "commonMappings.hpp" +#include "languages/Acsp/AcspWitness.hpp" +#include "languages/Bair/BairWitness.hpp" +#include "languages/Bair/BairInstance.hpp" +#include "witnessMappings.hpp" + +#include +#include + +namespace libstark{ +namespace BairToAcsp{ + +class witnessReduction{ +public: + static std::unique_ptr reduceWitness(const BairInstance& instance, const BairWitness& witness); + +protected: + typedef std::vector evaluation_t; + + static std::vector getEmbeddingMapping( const BairInstance& instance, const BairWitness& witness, const common& commonDef, const witnessMappings& witnessMapping); + static void mapChi(const BairInstance& instance, const BairWitness& witness, std::vector& mappings, const common& commonDef, const witnessMappings& witnessMapping); + static void mapNetwork(const BairInstance& instance, const BairWitness& witness, std::vector& mappings, const common& commonDef, const witnessMappings& witnessMapping); +}; + +} //namespace BairToAcsp +} //namespace libstark + + +#endif // Bair_TO_Acsp_WITNESS_REDUCTION_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Routing/BenesNetwork.cpp b/libstark/src/reductions/BairToAcsp/Routing/BenesNetwork.cpp new file mode 100644 index 0000000..b0f6d4b --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/BenesNetwork.cpp @@ -0,0 +1,180 @@ +/** + * @file BenesNetwork.cpp + * @brief This file implements algorithms needed for BenesNetwork construction + * + * # Claim # + * For any any given permutation \f$ \pi: \{0,1,\dots,2^k -1\} \to \{0,1,\dots,2^k -1\} \f$, + * there exists a Benes network of degree \f$ k \f$ that routs the data packets \f$ \{0,1,\dots,2^k -1\} \f$ using distinct + * nodes, such that a data packet \f$ d \f$ is in the last layer in the node labeled \f$ \omega \f$ + * if and only if the data packet \f$ \pi(d) \f$ is located in the first layer at the node labeled \f$ \omega \f$ + * + * # Proof # + * The proof is constructive, and well explained at + * http://pages.cs.wisc.edu/~tvrdik/10/html/Section10.html + * (under off-line permutaion routing) + * + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#include "BenesNetwork.hpp" +#include "MatrixNetwork.hpp" +#include "common/Infrastructure/Infrastructure.hpp" + +#include +#include +#include + +namespace libstark{ +namespace BairToAcsp{ + +using std::vector; +using std::move; +using std::set; +using std::unique_ptr; +using std::find; + +typedef RoutingNetwork::layerID_t layerID_t; +typedef RoutingNetwork::labelID_t labelID_t; +typedef RoutingNetwork::dataID_t dataID_t; +typedef TandemNetwork::netsVec_t netsVec_t; + +/** + * @brief A recursive function that routs a permutation, that is defined by its first and last layer, using Benes routing. This function is an implementation of the algorithm given in the proof to the claim in the file header. + * @param revBtrfly the reverse butterfly network part of the full Benes network (not only the one specific to this recursion step) + * @param btrfly the butterfly network part of the full Benes network (not only the one specific to this recursion step) + * @param current_k the degree of the Benes network relevant to the current recursion step. + * @param offset the offset of the Benes network that is routed in the current recursion state, relatively to the first node in each layer in the full Benes network. For example, when routing a Benes network of degree \f$ k \f$ we have at the first step offset=0, when it calls to itself again, it calls to itself once with offset=0, and another time with offset=\f$\frac{2^k}{2} = 2^{k-1}\f$ + */ +void static constructBenes_recursive(matrixNetwork& revBtrfly, matrixNetwork& btrfly, const layerID_t& current_k, const labelID_t& offset){ + + //constants relevant for current layer + const dataID_t numLabels = 1<& firstLayer = revBtrfly.data[currLayer]; + vector& secondLayer = revBtrfly.data[currLayer+1]; + const vector& lastLayer = btrfly.data[current_k]; + vector& preLastLayer = btrfly.data[current_k-1]; + + const layerID_t mask = 1<<(current_k-1); + + //Recursion stopping condition + if (numLabels == 2){ + secondLayer[offset] = firstLayer[offset]; + secondLayer[offset+1] = firstLayer[offset+1]; + preLastLayer[offset] = lastLayer[offset]; + preLastLayer[offset+1] = lastLayer[offset+1]; + return; + } + + set unrouted; + for (dataID_t i=0; i < numLabels; i++){ + unrouted.insert(i); + } + + + while (!unrouted.empty()){ + + //get source port to rout (not yet routed) + auto src_indx = offset + *(unrouted.begin()); + + while(unrouted.count(src_indx - offset) > 0){ + const dataID_t currData = firstLayer[src_indx]; + unrouted.erase(src_indx - offset); + + //route the source port to the "upper" network (clear bit) + { + labelID_t src_nxt = src_indx & ~mask; + secondLayer[src_nxt] = currData; + } + + //find destination of current source port to rout + auto dst_indx = find(lastLayer.begin(), lastLayer.end(),currData) - lastLayer.begin(); + + //route the destination port also to the "upper" network (clear bit) + { + labelID_t dst_nxt = dst_indx & ~mask; + preLastLayer[dst_nxt] = currData; + } + + //find the twin of the output port + auto dst_twin = dst_indx ^ mask; + const dataID_t twinData = lastLayer[dst_twin]; + + //route the twin of the destination port to the "lower" network (set bit) + { + labelID_t twin_nxt = dst_indx | mask; + preLastLayer[twin_nxt] = twinData; + } + + //find the source of the output port twin + auto src_twin = find(firstLayer.begin(), firstLayer.end(),twinData) - firstLayer.begin(); + + //route the source of the output twin port also to the "lower" network (set bit) + { + labelID_t twin_nxt = src_twin | mask; + secondLayer[twin_nxt] = twinData; + + //mark as routed by removing from "unrouted" set + unrouted.erase(src_twin - offset); + } + + //find twin of the source recently routed, and rout it + src_indx = src_twin ^ mask; + } + } + constructBenes_recursive(revBtrfly, btrfly,current_k-1, offset); + constructBenes_recursive(revBtrfly, btrfly,current_k-1, offset + numLabels/2); +} + +void BenesNetwork::rout(const permutation_t& permutation){ + if(!isPermutation(permutation)){ + _COMMON_FATAL("Can't rout given sequence, it is noot a permutation"); + } + + data_.reset(constructBenes(permutation)); + + isRouted = true; +} + +dataID_t BenesNetwork::getDataID(const layerID_t& l, const labelID_t& w) const{ + if (!isRouted){ + _COMMON_FATAL("Can't get data, network ot routed"); + } + return data_.getDataID(l,w); +} + +/** + * @brief constructBenes + * @param k degree of Benes network to construct + * @param permutation a sequence of integers that is assumed to be a permutation when restricted to \f$ 2^k \f$ first elements + * @return A vector of 2 routing networks, the first is a reverse butterfly, and the second is a butterfly, and when concatenated they form a Benes vector that routs the given permutation + */ +netsVec_t BenesNetwork::constructBenes(const permutation_t& permutation){ + const labelID_t labelsAmount = 1< reverseBtrfly(new matrixNetwork()), btrfly(new matrixNetwork()); + reverseBtrfly->setSize(k_+1,labelsAmount); + btrfly->setSize(k_+1,labelsAmount); + + for (labelID_t i = 0; i< labelsAmount; i++){ + reverseBtrfly->data[0][i] = i ; + btrfly->data[k_][i] = permutation.getElementByIndex(i); + } + constructBenes_recursive(*(reverseBtrfly.get()),*(btrfly.get()),k_,0); + + netsVec_t res; + res.push_back(move(reverseBtrfly)); + res.push_back(move(btrfly)); + return res; +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/BenesNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/BenesNetwork.hpp new file mode 100644 index 0000000..807c433 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/BenesNetwork.hpp @@ -0,0 +1,111 @@ +/** + * @file BenesNetwork.hpp + * @brief Benes network generator and representation + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef BENES_NETWORK_HPP__ +#define BENES_NETWORK_HPP__ + +#include "PermutationRoutingNet.hpp" +#include "TandemNetwork.hpp" +#include "common/langCommon/Sequence.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class BenesNetwork + * + * # Butterfly routing network # + * A butterfly routing network of degree \f$ k \f$ is a routing network with + * \f$ k+1 \f$ layers, namely \f$ \ell_0 , \ell_1 , \dots , \ell_k \f$, and + * \f$ 2^k \f$ nodes in each layer, labeled by \f$ {0 , 1 , \dots , 2^k} \f$. + * We would prefer to interpret the labels as binary vectors of \f$ k \f$ bits, + * which is \f$\{0,1\}^k\f$. + * We use the trivial bijection between the two sets, which maps an integer \f$ n \in [2^k] \f$ + * to its binary representation using exactly \f$ k \f$ bits. + * + * A node \f$ (\ell_i , \omega) \f$ can pass its data exactly to one of its two neighbours, + * which are \f$ n_1 = ( \ell_{i+1} , \omega) \f$ and \f$ n_1 = ( \ell_{i+1} , \omega \oplus e_{i+1}) \f$. + * + * For example, if \f$ k = 3 \f$ the neighbors of \f$ (\ell_0 , 011) \f$ are + * \f$ (\ell_1 , 011), (\ell_1 , 010) \f$, and the neighbors of \f$ (\ell_1 , 011) \f$ are + * \f$ (\ell_2 , 011), (\ell_2 , 001) \f$ + * + * # Reverse butterfly routing network # + * A reverse butterfly routing network of degree \f$ k \f$ is a routing network with + * \f$ k+1 \f$ layers, namely \f$ \ell_0 , \ell_1 , \dots , \ell_k \f$, and + * \f$ 2^k \f$ nodes in each layer, labeled by \f$ {0 , 1 , \dots , 2^k} \f$. + * We would prefer to interpret the labels as binary vectors of \f$ k \f$ bits, + * which is \f$\{0,1\}^k\f$. + * We use the trivial bijection between the two sets, which maps an integer \f$ n \in [2^k] \f$ + * to its binary representation using exactly \f$ k \f$ bits. + * + * A node \f$ (\ell_i , \omega) \f$ can pass its data exactly to one of its two neighbours, + * which are \f$ n_1 = ( \ell_{i+1} , \omega) \f$ and \f$ n_1 = ( \ell_{i+1} , \omega \oplus e_{k-i}) \f$. + * + * For example, if \f$ k = 3 \f$ the neighbors of \f$ (\ell_0 , 011) \f$ are + * \f$ (\ell_1 , 011), (\ell_1 , 111) \f$, and the neighbors of \f$ (\ell_1 , 011) \f$ are + * \f$ (\ell_2 , 011), (\ell_2 , 001) \f$ + * + * # Benes network # + * A Benes network of degree \f$k\f$ is a routing network with + * \f$ 2k + 1 \f$ layers, namely \f$ \ell_0 , \ell_1 , \dots , \ell_{2k+1} \f$, and + * \f$ 2^k \f$ nodes in each layer, labeled by \f$ {0 , 1 , \dots , 2^k} \f$. + * We would prefer to interpret the labels as binary vectors of \f$ k \f$ bits, + * which is \f$\{0,1\}^k\f$. + * We use the trivial bijection between the two sets, which maps an integer \f$ n \in [2^k] \f$ + * to its binary representation using exactly \f$ k \f$ bits. + * + * A Benes network is a concatenation of a reverse butterfly network, and a butterfly network, + * such that the central layer is common to both network (it is the last layer of the reverse butterfly network + * and the first layer of the butterfly network) + * + * ## Important fact ## + * For any any given permutation \f$ \pi: \{0,1,\dots,2^k -1\} \to \{0,1,\dots,2^k -1\} \f$, + * there exists a Benes network of degree \f$ k \f$ that routs the data packets \f$ \{0,1,\dots,2^k -1\} \f$ using distinct + * nodes, such that a data packet \f$ d \f$ is in the last layer in the node labeled \f$ \omega \f$ + * if and only if the data packet \f$ \pi(d) \f$ is located in the first layer at the node labeled \f$ \omega \f$ + * + * # This class # + * A class that represents a Benes permutation network for a given permutation + * An instance of this class is initialized using a degree \f$ k \f$ and a sequence of integers + * which assumed to represent a permutation when restricted to the first \f$ 2^k \f$ elements, + * and constructs a Benes network that routs the given permutation. + * + * @note The generated respects the order of data in the first layer, meaning the data in \f$ (\ell_0 , \omega) \f$ + * is \f$ \omega \f$ (it's decimal representation) + */ +class BenesNetwork: public PermutationRoutingNet { +public: + BenesNetwork(const layerID_t& k) + :PermutationRoutingNet(1<= first_identity) { + return reverseBits(w)>>(sizeof(w)*8 - k_); + } + + layerID_t curr_l = l; + labelID_t curr_w = w; + + if (curr_l > first_left3){ + curr_w = fleft_.getDataID(curr_l-first_left3, curr_w); + curr_l = first_left3; + } + + if (curr_l > first_right2){ + curr_w = fright_.getDataID(curr_l-first_right2, curr_w); + curr_l = first_right2; + } + + if (curr_l > first_left1){ + curr_w = fleft_.getDataID(curr_l-first_left1, curr_w); + curr_l = first_left1; + } + + return curr_w; +} +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/BitReverseButterfliesNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/BitReverseButterfliesNetwork.hpp new file mode 100644 index 0000000..afc446b --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/BitReverseButterfliesNetwork.hpp @@ -0,0 +1,62 @@ +/** + * @file BitReverseButterfliesNetwork.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef BIT_REVERSE_BUTTERFLIES_NETWORK_HPP__ +#define BIT_REVERSE_BUTTERFLIES_NETWORK_HPP__ + +#include "RoutingNetwork.hpp" +#include "FoldNetwork.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class BitReverseButterfliesNetwork + * @brief A network that implements the bit reverse permutation using 2 butterfly networks + * + * We notice the following three facts: + * + * - The composition \f$ foldLeft \circ foldRight \circ foldLeft \f$ is exactly the bit reverse permutation + * + * - The foldLeftNetwork of degree \f$k\f$ is a restriction of the first half of a butterfly network of degree \f$k\f$ (layers \f$ \ell_0 \dots \ell_{\lceil \frac{k}{2} \rceil} \f$) + * + * - The foldRightNetwork of degree \f$k\f$ is a restriction of the second half of a butterfly network of degree \f$k\f$ (layers \f$ \ell_{\lceil \frac{k}{2} \rceil \dots \ell_k} \f$) + * + * From all above we conclude that we can route the bit reverse permutation on 1.5 butterfly networks, but instead of using a concatenation, we need in this case + * composition (the permutation of a network effects the location of the input data in the next network network). We may also implement the bit reverse + * permutation using 2 butterfly networks, by 'finishing' the second network using the identity permutation (each node passes the data to the node with the same + * label in the next layer). + * + * This class implements exactly the described 2 butterflies bit reverse network, it is initialized using the wanted degree \f$k\f$. + */ +class BitReverseButterfliesNetwork : public RoutingNetwork{ +public: + BitReverseButterfliesNetwork(const layerID_t& k): + k_(k),fleft_(k),fright_(k){;} + + layerID_t width() const {return 1 + 2*k_;} // = 1 + ceil(k/2) + labelID_t height() const {return 1< + +namespace libstark{ +namespace BairToAcsp{ + +using std::vector; + +typedef RoutingNetwork::layerID_t layerID_t; +typedef RoutingNetwork::labelID_t labelID_t; +typedef RoutingNetwork::dataID_t dataID_t; + +namespace{ + +/*** + * @class bitReverse + * @brief permutes a given sequence by bit reversing its values + * + * We define \f$ rev : {0,1}^{numBits} \to {0,1}^{numBits} \f$ as + * the bit reversal operator. + * Let \f$ A \f$ be a given sequence (named "src" in the constructor), + * this class defines a new sequence \f$ B \f$ such that + * \f$ b_i = rev(a_i) \f$ + * + * @note + * For internal usage only + * As one might see, if an instance leaves outside of the scope it was initialized in + * it may cause a dangeling reference (to origSequence_) + * it is defined with the knowledge that such a thing will not happen + * because any instance of this class dies before the "rout" method ends + * and the Sequence "src" is a parameter to this function + */ +class bitReverse : public Sequence{ +public: + bitReverse(const Sequence& src, const layerID_t& numBits) + :src_(src),numBits_(numBits){}; + + size_t getElementByIndex(index_t index)const { + const auto origVal = src_.getElementByIndex(index); + return reverseBits(origVal)>>(8*sizeof(origVal)-numBits_); + } +private: + const Sequence& src_; + const size_t numBits_; +}; + +} + +dataID_t BtrflyBasedPermutation::getDataID(const layerID_t& l, const labelID_t& w) const{ + const layerID_t first_benesFirstHalf = 0; + const layerID_t first_bitReverse = k_; + const layerID_t first_benesSecondHalf = 3*k_; + + layerID_t curr_l = l; + labelID_t curr_w = w; + + if (l >= first_benesSecondHalf) { + curr_w = benes_.getDataID(l - first_benesSecondHalf + k_,w); + } + else{ + if (curr_l > first_bitReverse){ + curr_w = bitRev_.getDataID(curr_l-first_bitReverse, curr_w); + curr_l = first_bitReverse; + } + + if (curr_l >= first_benesFirstHalf){ + curr_w = reverseBits(curr_w)>>(sizeof(curr_w)*8 - k_); + curr_w = benes_.getDataID(curr_l-first_benesFirstHalf, curr_w); + curr_l = first_benesFirstHalf; + } + } + + //fix the permutation done on the first layer indexes (bit reverse permutation) + //by bit reversing all the data in the network + //this is mandatory, because the second half of the Benes network 'assumes' the data + //inserted to the first half with respect for the order, while before + //this fix that order is shuffled + dataID_t res = reverseBits(curr_w)>>(sizeof(curr_w)*8 - k_); + + return res; +} + +void BtrflyBasedPermutation::rout(const permutation_t& permutation){ + benes_.rout(bitReverse(permutation,k_)); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/BtrflyBasedPermutation.hpp b/libstark/src/reductions/BairToAcsp/Routing/BtrflyBasedPermutation.hpp new file mode 100644 index 0000000..514cb56 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/BtrflyBasedPermutation.hpp @@ -0,0 +1,52 @@ +#ifndef BTRFLY_BASED_PERMUTATION_HPP__ +#define BTRFLY_BASED_PERMUTATION_HPP__ + +#include "BenesNetwork.hpp" +#include "BitReverseButterfliesNetwork.hpp" +#include "PermutationRoutingNet.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class BtrflyBasedPermutation + * @brief A class of networks that can implement any permutation over \f$2^k\f$ elements, using a composition of 4 butterfly networks of degree \f$k\f$ + * + * Implementation details: + * We know that we can rout any permutation on a Benes network, which is a composition of a reverse butterfly and a butterfly networks. + * We use the graph isomorphism from reverse butterfly of degree \f$k\f$ to a butterfly network of the same degree + * \f$ \phi(\ell_i,\omega) = (\ell_i,\omega^R)\f$, this isomorphism maps a reverse butterfly to a butterfly that routs the same permutation in respect to the data, + * but messes up the order of labels ,they are massed up with the bit reverse permutation. So in order to attach the result of this isomorphism to the butterfly network in the second half of the Benes network, + * and to keep the permutation as it was originally in the Benes network, we need to permute the labels using the bit reverse permutation (which is the inverse of itself) + * before the new butterfly network (the image of the reverse butterfly), and after it. + * For this we can use BitReverseButterfliesNetwork that does each reverse using 2 butterfly network, + * this way we get a network of 6 butterfly networks composed, that routs the original permutation. + * We notice that the first 2 butterfly networks in this network is just the bit reverse permutation, + * instead of using those we could just change the permutation using only the last for butterfly networks, + * so instead of routing the permutation \f$ \pi \f$ on the Benes network we rout \f$ \tilde{\pi} \f$ which + * defined by \f$ \tilde{\pi}(\omega) = \pi(\omega^R) \f$. + * This change of permutation is done by the class indxBitReverse. + */ +class BtrflyBasedPermutation : public PermutationRoutingNet { +public: + BtrflyBasedPermutation(const labelID_t k) + :PermutationRoutingNet(1<{ +public: + shiftRight(const Sequence& src, const layerID_t& numBits) + :src_(src),numBits_(numBits){}; + + size_t getElementByIndex(index_t index)const { + const labelID_t mask = (1<>(numBits_-1))) & mask; + const auto origVal = src_.getElementByIndex(shifted_index); + const auto shifted_data = ((origVal>>1) | (origVal<<(numBits_-1))) & mask; + + return shifted_data; + } +private: + const Sequence& src_; + const size_t numBits_; +}; + +} + +dataID_t DeBruijnPermutationNet::getDataID(const layerID_t& l, const labelID_t& w) const{ + const size_t shift_size = (l+k_-1)%k_; + const labelID_t mask = (1<>(k_-shift_size))) & mask; + + //get data from btrfly + const dataID_t btrfly_data = btrfly_.getDataID(l,btrfly_w); + + //shifted data - to keep the first column ordered + const auto shifted_data = ((btrfly_data<<1) | (btrfly_data>>(k_-1))) & mask; + + return shifted_data; +} + +void DeBruijnPermutationNet::rout(const permutation_t& permutation){ + btrfly_.rout(shiftRight(permutation,k_)); +} + +short DeBruijnPermutationNet::routingBit(const layerID_t& l, const labelID_t& w) const{ + + const labelID_t mask = (1<>1) | (w<<(k_-1))) & mask; + const auto neighbor2_label = neighbor1_label ^ 1; + + return getDataID(l,w) == getDataID(l+1,neighbor2_label); +} + + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/DeBruijnPermutationNet.hpp b/libstark/src/reductions/BairToAcsp/Routing/DeBruijnPermutationNet.hpp new file mode 100644 index 0000000..3be799b --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/DeBruijnPermutationNet.hpp @@ -0,0 +1,77 @@ +/** + * @file DeBruijnPermutationNet.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef DE_BRUIJN_PERMUTATION_NET_HPP__ +#define DE_BRUIJN_PERMUTATION_NET_HPP__ + +#include "BtrflyBasedPermutation.hpp" +#include "Deg2PermutationRoutingNet.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class DeBruijnPermutationNet + * @brief A network that can rout any permutation using 4 De Bruijn networks + * + * # De Bruijn routing network # + * A De Bruijn routing network of degree \f$ k \f$ is a routing network with + * \f$ k+1 \f$ layers, namely \f$ \ell_0 , \ell_1 , \dots , \ell_k \f$, and + * \f$ 2^k \f$ nodes in each layer, labeled by \f$ {0 , 1 , \dots , 2^k} \f$. + * We would prefer to interpret the labels as binary vectors of \f$ k \f$ bits, + * which is \f$\{0,1\}^k\f$. + * We use the trivial bijection between the two sets, which maps an integer \f$ n \in [2^k] \f$ + * to its binary representation using exactly \f$ k \f$ bits. + * + * We define \f$ sr : \{0,1\}^k \to \{0,1\}^k \f$ as the cyclic shift right. + * + * A node \f$ (\ell_i , \omega) \f$ can pass its data exactly to one of its two neighbours, + * which are \f$ n_1 = ( \ell_{i+1} , sr(\omega)) \f$ and \f$ n_1 = ( \ell_{i+1} , sr(\omega) \oplus e_1) \f$. + * + * For example, if \f$ k = 3 \f$ the neighbors of \f$ (\ell_0 , 011) \f$ are + * \f$ (\ell_1 , 101), (\ell_1 , 100) \f$, and the neighbors of \f$ (\ell_1 , 101) \f$ are + * \f$ (\ell_2 , 110), (\ell_2 , 111) \f$ + * + * # De-Bruijn to Butterfly isomorphism # + * The isomorphism is between network of degree \f$k\f$ and defined by + * \f$ \phi(\ell_i , \omega) = (\ell_i , sr^{(k + 1 -l) \mod k}(\omega)) \f$ + * + * # What is this class # + * This class uses the isomorphism mentioned above. It hold an instance of a 4 butterflies networks + * that routes the needed permutation, and translates the addresses using the mentioned isomorphism to + * get a De Bruijn from it. + */ +class DeBruijnPermutationNet : public Deg2PermutationRoutingNet { +public: + DeBruijnPermutationNet(const labelID_t& k) + :Deg2PermutationRoutingNet(1<>(k_-l); + + //calculate the reverse of the folded data + labelID_t revData = reverseBits(foldedData)>>(8*sizeof(labelID_t) - l); + + //calculate mask for middle bit: + //if $k$ is odd, there is a bit in the middle that + //should not change, without a special + //tritment it will be allwayes xored with itself, so + //it will be alwayes cleared. + //We calculate a mask that sets it if needed + const dataID_t middleMask = (k_%2)<<(k_/2); + return (w ^ revData) | (w & middleMask) ; +} + +dataID_t foldRightNetwork::getDataID(const layerID_t& l, const labelID_t& w) const{ + dataID_t mask = (1<>(k_/2 - l); + + //calculate the reverse of the folded data + labelID_t revData = reverseBits(foldedData)>>(8*sizeof(labelID_t) - l - (k_/2 + k_%2)); + + //calculate mask for middle bit: + //if $k$ is odd, there is a bit in the middle that + //should not change, without a special + //tritment it will be allwayes xored with itself, so + //it will be alwayes cleared. + //We calculate a mask that sets it if needed + const dataID_t middleMask = (k_%2)<<(k_/2); + return (w ^ revData) | (w & middleMask) ; +} + + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/FoldNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/FoldNetwork.hpp new file mode 100644 index 0000000..d038238 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/FoldNetwork.hpp @@ -0,0 +1,118 @@ +/** + * @file FoldNetwork.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef FOLD_NETWORK_HPP__ +#define FOLD_NETWORK_HPP__ + +#include "RoutingNetwork.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class foldLeftNetwork + * @brief The network that implements folding left operation (permutation) + * + * The fold left operation on a word \f$ \omega \in \{0,1\}^k \f$ is defined + * in the following way: we represent \f$ \omega \f$ as \f$ \omega = xyz \f$ such that + * \f$ x,z \in \{0,1\}^{\lfloor \frac{k}{2} \rfloor} \f$ and \f$ y \in \{0,1,\varepsilon\}\f$, + * so the mapping is defined by \f$ xyz \mapsto x \cdot y \cdot ( z \oplus x^R )\f$ where \f$x^R \f$ + * is the reverse of \f$x\f$. + * + * The foldLeft network of degree \f$k\f$ is a network with \f$ 1 + \lceil \frac{k}{2} \rceil \f$ layers, + * namely \f$ \ell_0 , \ell_1 ,\dots , \ell_{\lceil \frac{k}{2} \rceil} \f$, and \f$ 2^k \f$ nodes per layer, + * labeled by \f$ {0 , 1 , \dots , 2^k} \f$. + * We would prefer to interpret the labels as binary vectors of \f$ k \f$ bits, + * which is \f$\{0,1\}^k\f$. + * We use the trivial bijection between the two sets, which maps an integer \f$ n \in [2^k] \f$ + * to its binary representation using exactly \f$ k \f$ bits. + * + * A node \f$ (\ell_i , \omega) \f$ can pass its data exactly to one neighbour. + * which is \f$ n = ( \ell_{i+1} , \omega) \f$ if \f$ i+1 = k-i \vee \omega_{i+1} = \omega_{i+1} \oplus \omega_{k-i} \f$ or to \f$ n = ( \ell_{i+1} , \omega \oplus e_{i+1}) \f$ otherwise. + * + * For example, if \f$ k = 3 \f$ the neighbor of \f$ (\ell_0 , 011) \f$ is + * \f$ (\ell_1 , 010) \f$, and the neighbor of \f$ (\ell_1 , 010) \f$ is + * \f$ (\ell_2 , 010) \f$ + * + * Notice this defines a routing in distinct vertexes of the permutation foldLeft. + * + * This class implements the above defined network, and constructs it for a given dimension \f$k\f$. + * + * @note The generated respects the order of data in the first layer, meaning the data in \f$ (\ell_0 , \omega) \f$ + * is \f$ \omega \f$ (it's decimal representation) + */ +class foldLeftNetwork : public RoutingNetwork{ +public: + foldLeftNetwork(const layerID_t& k):k_(k){;} + + layerID_t width() const {return 1 + k_/2 + k_%2;} // = 1 + ceil(k/2) + labelID_t height() const {return 1<{ +public: + rightShiftedIndexes(const Sequence& src, const layerID_t& numBits, const layerID_t& numShifts) + :origSequence_(src), numBits_(numBits), numShifts_(numShifts % numBits){;} + + size_t getElementByIndex(index_t index)const { + const index_t mask = (1<>numShifts_) | (index<<(numBits_ - numShifts_))) & mask; + const auto data = origSequence_.getElementByIndex(shifted_index); + return data; + } +private: + const Sequence& origSequence_; + const layerID_t numBits_; + const layerID_t numShifts_; +}; + +} //nameless scope + +LongDeBruijnPermutationNet::LongDeBruijnPermutationNet(const labelID_t& k) : + Deg2PermutationRoutingNet(1<>(k_ - numShifts))) & mask; + const auto lastDbColumn = DB_net_.width()-1; + return getDataID(lastDbColumn, shifted_w); +} + +short LongDeBruijnPermutationNet::routingBit(const layerID_t& l, const labelID_t& w) const{ + //If the vertex is not in the extension, access directly the DeBruijn network + if (l < DB_net_.width()-1) return DB_net_.routingBit(l,w); + + //Otherwise, return a constant 0 + //because the extension has constant routing + return 0; +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/LongDeBruijnPermutationNet.hpp b/libstark/src/reductions/BairToAcsp/Routing/LongDeBruijnPermutationNet.hpp new file mode 100644 index 0000000..3e6ed85 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/LongDeBruijnPermutationNet.hpp @@ -0,0 +1,51 @@ +/** + * @file LongDeBruijnPermutationNet.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 13/2/2014 + * Company SCIPR-LAB + * Copyright Copyright (c) 2014, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef LONG_DE_BRUIJN_PERMUTATION_NET_HPP__ +#define LONG_DE_BRUIJN_PERMUTATION_NET_HPP__ + +#include "DeBruijnPermutationNet.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class LongDeBruijnPermutationNet + * @brief A network that can rout any permutation using 4 De Bruijn networks + reminder to make the amount of columns a power of 2 + * + * It is simply a DeBruijn network, in tandem with a DeBruijn network that routs in constant direction: + * each vertex \f$ ( \ell_i , \omega ) \f$ in the additional network has exactly one neighbor : \f$ (\ell_{i+1}, sr(\omega)) \f$ + * + * This is used for the arighmetization step, where the amount of layers must be a power of 2 + */ +class LongDeBruijnPermutationNet : public Deg2PermutationRoutingNet{ +public: + LongDeBruijnPermutationNet(const labelID_t& k); + layerID_t width() const {return width_;} + labelID_t height() const {return DB_net_.height();} + dataID_t getDataID(const layerID_t& l, const labelID_t& w) const; + void rout(const permutation_t& permutation); + short routingBit(const layerID_t& l, const labelID_t& w) const; + +private: + DeBruijnPermutationNet DB_net_; + const layerID_t k_; + const layerID_t width_; +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //LONG_DE_BRUIJN_PERMUTATION_NET_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Routing/LongSymmetricDeBruijnNetwork.cpp b/libstark/src/reductions/BairToAcsp/Routing/LongSymmetricDeBruijnNetwork.cpp new file mode 100644 index 0000000..5673473 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/LongSymmetricDeBruijnNetwork.cpp @@ -0,0 +1,100 @@ +#include "LongSymmetricDeBruijnNetwork.hpp" +#include "common/Infrastructure/Infrastructure.hpp" + +#include + +using Infrastructure::POW2; +using Infrastructure::CEIL; +using Infrastructure::Log2; + +namespace libstark{ +namespace BairToAcsp{ + +typedef RoutingNetwork::layerID_t layerID_t; +typedef RoutingNetwork::labelID_t labelID_t; +typedef RoutingNetwork::dataID_t dataID_t; + +LongSymmetricDeBruijnNetwork::LongSymmetricDeBruijnNetwork(const layerID_t& k): + Deg2PermutationRoutingNet(1<>shift_size) | (w<<(k_-shift_size))) & mask; + } + else{ + //second half, extension + //rout as straight DeBruijn + const size_t shift_size = width()-(k_+1)-l; + baseLabel = ((w>>shift_size) | (w<<(k_-shift_size))) & mask; + } + + return baseNet_.getDataID(k_,baseLabel); +} + +dataID_t LongSymmetricDeBruijnNetwork::getDataID(const short networkID, const layerID_t& l, const labelID_t& w)const{ + assert( l < getWingWidth()); + switch(networkID){ + case 0: return getDataID(l,w); + case 1: return getDataID((width()-1)-l,w); + default: _COMMON_FATAL("Unsupported network ID"); + } +} + +short LongSymmetricDeBruijnNetwork::routingBit(const layerID_t& l, const labelID_t& w) const{ + + const labelID_t mask = (1<>(k_-1))) & mask; + const auto shifted1 = ((1<<1) | (1>>(k_-1))) & mask; + neighbor2_label = neighbor1_label ^ shifted1; + } + else{ + //second half - straight DeBruijn + neighbor1_label = ((w>>1) | (w<<(k_-1))) & mask; + neighbor2_label = neighbor1_label ^ 1; + } + + return getDataID(l,w) == getDataID(l+1,neighbor2_label); +} + +short LongSymmetricDeBruijnNetwork::routingBit(const short networkID, const layerID_t& l, const labelID_t& w) const{ + + const labelID_t mask = (1<>(k_-1))) & mask; + + return getDataID(networkID,l,w) == getDataID(networkID,l+1,neighbor2_label); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/LongSymmetricDeBruijnNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/LongSymmetricDeBruijnNetwork.hpp new file mode 100644 index 0000000..62b84ff --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/LongSymmetricDeBruijnNetwork.hpp @@ -0,0 +1,54 @@ +#ifndef LONG_SYMMETRIC_DEBRUIJN_NETWORK +#define LONG_SYMMETRIC_DEBRUIJN_NETWORK + +#include "SymmetricDeBruijnNetwork.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class LongSymmetricDeBruijnNetwork + * + * This is an extended version of the SymmetricDeBruijnNetwork, + * it extends such a network symmetrically in the center of it, + * adding columns, such that it would become a network of + * a reverse DeBruijn and a DeBruijn in tandem, such that each + * side is of a width that is of the form \f$ 2^m -1 \f$ for + * the unique \f$m\f$ that satisfies \f$ 2^{m-1} -1 < k+1 \le 2^m -1 \f$. + * In total, the length of such a network is \f$ 2(2^m -1)-1 \f$ (the central column is common) + */ + +class LongSymmetricDeBruijnNetwork : public Deg2PermutationRoutingNet { +public: + LongSymmetricDeBruijnNetwork(const layerID_t& k); + + layerID_t width() const {return 2*halfWidth_ - 1;} //The central column is common to both halfs + labelID_t height() const {return baseNet_.height();} + dataID_t getDataID(const layerID_t& l, const labelID_t& w)const; + short routingBit(const layerID_t& l, const labelID_t& w)const; + + void rout(const permutation_t& permutation){baseNet_.rout(permutation);} + + /// + /// The network can be seen as two inverse DeBruijn networks + /// such that the last column of both are same, + /// and the permutation it routs is exactly the mapping + /// from a value in some label \f$ \omega \f$ in the first column of + /// the first network, to the value in label \f$ \omega \f$ in + /// the first column of the second label. + /// The following function give this interpretation to the network. + + layerID_t getWingWidth()const {return halfWidth_;} + dataID_t getDataID(const short networkID, const layerID_t& l, const labelID_t& w)const; + short routingBit(const short networkID, const layerID_t& l, const labelID_t& w)const; + +private: + SymmetricDeBruijnNetwork baseNet_; + const labelID_t k_; + const layerID_t halfWidth_; +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //LONG_SYMMETRIC_DEBRUIJN_NETWORK diff --git a/libstark/src/reductions/BairToAcsp/Routing/MatrixNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/MatrixNetwork.hpp new file mode 100644 index 0000000..23ed7a2 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/MatrixNetwork.hpp @@ -0,0 +1,69 @@ +/** + * @file MatrixNetwork.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef MATRIX_NETWORK_HPP__ +#define MATRIX_NETWORK_HPP__ + +#include "RoutingNetwork.hpp" + +#include +#include + +namespace libstark{ +namespace BairToAcsp{ +/** + * @class matrixNetwork + * @brief A trivial implementation to RoutingNetwork interface, using a matrix with no constraints + * + * This class is for internal use of the routing networks library only, and should not be exposed as some interface outside, + * because is is made with poor interface, no encapsulation, and state may be non consistent + * (the dimensions may be changed independently to the 'data' vector). One who uses it must be responsible + * for keeping the state consistent. + */ +class matrixNetwork : public RoutingNetwork{ +public: + std::vector> data; + layerID_t numLayers; + layerID_t numNodes; + + layerID_t width()const{ + return numLayers; + } + + labelID_t height()const{ + return numNodes; + } + + dataID_t getDataID(const layerID_t& l, const labelID_t& w)const{ + assert(l >= 0); + assert(l < data.size()); + assert(w >= 0); + assert(w < data[l].size()); + return data[l][w]; + } + + void setSize(const layerID_t& layersAmount, const labelID_t& labelsAmount){ + numLayers = layersAmount; + numNodes = labelsAmount; + data.resize(numLayers); + for (auto& layer : data){ + layer.resize(numNodes); + } + } +}; +} //namespace BairToAcsp +} //namespace libstark + +#endif //MATRIX_NETWORK_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Routing/PermutationRoutingNet.cpp b/libstark/src/reductions/BairToAcsp/Routing/PermutationRoutingNet.cpp new file mode 100644 index 0000000..bfa6fef --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/PermutationRoutingNet.cpp @@ -0,0 +1,54 @@ +/** + * @file PermutationRoutingNet.cpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 12/04/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#include "PermutationRoutingNet.hpp" + +#include + +using std::vector; + +namespace libstark{ +namespace BairToAcsp{ + +bool PermutationRoutingNet::isPermutation(const permutation_t& permutation)const{ + //We check the mapping is a bijection (permutation) + //by checking that it is injective + + //inImage[i] will be set to 'true' iff + //there exists j that is mapped to it + vector inImage(numElements_); + + //initialization + for(labelID_t i=0; i< numElements_; i++) inImage[i] = false; + + //Check if is an injection + for(labelID_t i=0; i< numElements_; i++){ + dataID_t img = permutation.getElementByIndex(i); + + //check the image is inside {0 ... numElements-1} + if ((img < 0) || (img >= numElements_)) return false; + + //check no element was mapped to img before (validate injectivity) + if (inImage[img]) return false; + + //mark in image + inImage[img] = true; + } + + return true; +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/PermutationRoutingNet.hpp b/libstark/src/reductions/BairToAcsp/Routing/PermutationRoutingNet.hpp new file mode 100644 index 0000000..f89563b --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/PermutationRoutingNet.hpp @@ -0,0 +1,42 @@ +/** + * @file PermutationRoutingNet.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 12/04/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef PERMUTATION_ROUTING_NET_HPP__ +#define PERMUTATION_ROUTING_NET_HPP__ + +#include "RoutingNetwork.hpp" +#include "common/langCommon/Sequence.hpp" + +#include + +namespace libstark{ +namespace BairToAcsp{ + +class PermutationRoutingNet : public RoutingNetwork { +public: + typedef Sequence permutation_t; + + PermutationRoutingNet(const labelID_t numElements):numElements_(numElements){;} + virtual void rout(const permutation_t& permutation) = 0; +protected: + bool isPermutation(const permutation_t& permutation)const; + + const labelID_t numElements_; +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif // PERMUTATION_ROUTING_NET_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Routing/RoutingNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/RoutingNetwork.hpp new file mode 100644 index 0000000..84e8502 --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/RoutingNetwork.hpp @@ -0,0 +1,93 @@ +/** + * @file routingNetwork.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef ROUTING_NETWORK_HPP__ +#define ROUTING_NETWORK_HPP__ + +#include +#include "common/Infrastructure/Infrastructure.hpp" +#include "common/Utils/ErrorHandling.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class RoutingNetwork + * @brief An interface for routing networks + * + * A routing network with no constraints on, might be thought as + * a matrix of nodes, each node holds some arbitrary data. + * This may sound weird, but there is no constraints on + * on the data either. It may appear from no-where and disappear to nowhere. + * Such a definition is used in order to get better performance, + * because constraints defined must be enforced, + * but we are not interested in doing that, + * because any case, the initialization of classes implementing + * this interface is done (in practical cases) using automatic + * algorithms that build the network, such that it satisfies + * all constraints that should be satisfied. + * + * The main difference between a routing network and a matrix + * is the terminology used. + * + * The matrix columns are named network layers. It is expected that + * data is passed in the network from one layer to the next layer + * (although this is not enforced by this interface, as mentioned) + * + * Instead of rows, as we have in matrixes, we have nodes in each + * layer, each node is labeled by a unique label (in the specific layer), + * the labels are \f$ \{0 \dots (<\text{number of nodes per layer}>-1)\} \f$. + * + * One may ask 'what is the data in the node, on layer \f$ \ell \f$ and label \f$\omega\f$'. + * the matrix-kind parallel question is 'what is the value of the cell in column \f$ \ell \f$ and row \f$ \omega \f$ ' + * or simply, for a matrix \f$M\f$ that is exactly \f$M_{\ell,\omega}\f$ + */ +class RoutingNetwork{ +public: + typedef uint64_t layerID_t; + typedef uint64_t labelID_t; + typedef labelID_t dataID_t; + + virtual layerID_t width() const = 0; + virtual labelID_t height() const = 0; + virtual dataID_t getDataID(const layerID_t& l, const labelID_t& w) const = 0; + + virtual ~RoutingNetwork(){;} +}; + +// +//A common method used by some routing networks constructions +// +template +T reverseBits(const T& src){ + T res = src; + const auto numBits = sizeof(T)*8; + + switch(numBits){ + case 64: res = ((res & 0xffffffff00000000)>>32) | ((res & 0x00000000ffffffff)<<32); + case 32: res = ((res & 0xffff0000ffff0000)>>16) | ((res & 0x0000ffff0000ffff)<<16); + case 16: res = ((res & 0xff00ff00ff00ff00)>>8) | ((res & 0x00ff00ff00ff00ff)<<8); + case 8 : res = ((res & 0xf0f0f0f0f0f0f0f0)>>4) | ((res & 0x0f0f0f0f0f0f0f0f)<<4); + case 4 : res = ((res & 0xcccccccccccccccc)>>2) | ((res & 0x3333333333333333)<<2); + case 2 : res = ((res & 0xaaaaaaaaaaaaaaaa)>>1) | ((res & 0x5555555555555555)<<1); break; + default : _COMMON_FATAL("Unsupported variable size for bit reversing (if it is because we have an archtectore with 128 bit or more than 'WOW!' and the fix should be trivial, otherwise this should not happen)"); + } + return res; +} + +} //namespace BairToAcsp +} //namespace libstark + +#endif //ROUTING_NETWORK_HPP__ diff --git a/libstark/src/reductions/BairToAcsp/Routing/SymmetricDeBruijnNetwork.cpp b/libstark/src/reductions/BairToAcsp/Routing/SymmetricDeBruijnNetwork.cpp new file mode 100644 index 0000000..621d38e --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/SymmetricDeBruijnNetwork.cpp @@ -0,0 +1,106 @@ +#include "SymmetricDeBruijnNetwork.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +typedef RoutingNetwork::layerID_t layerID_t; +typedef RoutingNetwork::labelID_t labelID_t; +typedef RoutingNetwork::dataID_t dataID_t; + +namespace{ + +/*** + * @class fixIndexes + * @brief permutes a given sequence for a specific fix needed for DeBruijn network + * this is made to have the first column allways ordered in increasing order 0,1,2,... + * + * We define \f$ sr : {0,1}^{numBits} \to {0,1}^{numBits} \f$ as + * the cyclic shift right operator. + * And we define \f$ sl : {0,1}^{numBits} \to {0,1}^{numBits} \f$ as + * the cyclic shift left operator. + * Let \f$ A \f$ be a given sequence (named "src" in the constructor), + * this class defines a new sequence \f$ B \f$ such that + * \f$ b_i = sr(a_{sl(i)}) \f$ + * + * @note + * For internal usage only + * As one might see, if an instance leaves outside of the scope it was initialized in + * it may cause a dangeling reference (to origSequence_) + * it is defined with the knowledge that such a thing will not happen + * because any instance of this class dies before the "rout" method ends + * and the Sequence "src" is a parameter to this function + */ +class shiftRight : public Sequence{ +public: + shiftRight(const Sequence& src, const layerID_t& numBits) + :src_(src),numBits_(numBits){}; + + size_t getElementByIndex(index_t index)const { + const labelID_t mask = (1<>(numBits_-1))) & mask; + const auto origVal = src_.getElementByIndex(shifted_index); + const auto shifted_data = ((origVal>>1) | (origVal<<(numBits_-1))) & mask; + + return shifted_data; + } +private: + const Sequence& src_; + const size_t numBits_; +}; + +} + +void SymmetricDeBruijnNetwork::rout(const permutation_t& permutation){ + benes_.rout(shiftRight(permutation,k_)); +} + +short SymmetricDeBruijnNetwork::routingBit(const layerID_t& l, const labelID_t& w) const{ + + const labelID_t mask = (1<>(k_-1))) & mask; + const auto shifted1 = ((1<<1) | (1>>(k_-1))) & mask; + neighbor2_label = neighbor1_label ^ shifted1; + } + else{ + //second half - straight DeBruijn + neighbor1_label = ((w>>1) | (w<<(k_-1))) & mask; + neighbor2_label = neighbor1_label ^ 1; + } + + return getDataID(l,w) == getDataID(l+1,neighbor2_label); +} + +dataID_t SymmetricDeBruijnNetwork::getDataID(const layerID_t& l, const labelID_t& w)const { + + //Find the original w, to get data from the Benes network + labelID_t orig_w; + if ( l < k_ ) orig_w = btrflyToDebruijn(k_-l,w); + else orig_w = btrflyToDebruijn(l-k_,w); + + //get the data + const auto btrfly_data = benes_.getDataID(l,orig_w); + + //shifted data - to keep the first column ordered + const labelID_t mask = (1<>(k_-1))) & mask; + + return shifted_data; +} + +labelID_t SymmetricDeBruijnNetwork::btrflyToDebruijn(const layerID_t& l, const labelID_t& w)const{ + const size_t shift_size = (l+k_-1)%k_; + const labelID_t mask = (1<>(k_-shift_size))) & mask; + return btrfly_w; +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/SymmetricDeBruijnNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/SymmetricDeBruijnNetwork.hpp new file mode 100644 index 0000000..38d170e --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/SymmetricDeBruijnNetwork.hpp @@ -0,0 +1,45 @@ +#ifndef SYMMETRIC_DEBRUIJN_NETWORK +#define SYMMETRIC_DEBRUIJN_NETWORK + +#include "BenesNetwork.hpp" +#include "Deg2PermutationRoutingNet.hpp" + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class SymmetricDeBruijnNetwork + * + * A merge between a DeBruijn network and Benes network. + * Just as Benes network is a reversed Butterfly and a Butterfly in tandem, + * a symmetric DeBruijn network is a reversed DeBruijn and a DeBruijn in tandem. + * such a network is isomorphic to a Benes network, in particular it can rout + * any permutation over a set of element (of power of 2 cardinality) on distinct paths. + */ + +class SymmetricDeBruijnNetwork : public Deg2PermutationRoutingNet { +public: + SymmetricDeBruijnNetwork(const layerID_t& k): Deg2PermutationRoutingNet(1<width() - 1; + //-1 because for each network, the first layer is + //shared with the last layer of previouse network (if there is such) + } + return amount; +} + +labelID_t TandemNetwork::height()const{ + //It assumes all the networks has the same amount of nodes + //per layer. + //Otherwise it does not describe a valid routing network + if (networks_.size() > 0) return networks_[0]->height(); + return 0; +} + +dataID_t TandemNetwork::getDataID(const layerID_t& l, const labelID_t& w)const{ + layerID_t i = 0; + for (const auto& network : networks_){ + if (i+network->width() > l) return network->getDataID(l-i,w); + i += network->width() - 1; + } + _COMMON_FATAL("Routing Network : out of range"); +} + +void TandemNetwork::reset(netsVec_t&& networks){ + networks_ = move(networks); +} + +} //namespace BairToAcsp +} //namespace libstark diff --git a/libstark/src/reductions/BairToAcsp/Routing/TandemNetwork.hpp b/libstark/src/reductions/BairToAcsp/Routing/TandemNetwork.hpp new file mode 100644 index 0000000..a29d8de --- /dev/null +++ b/libstark/src/reductions/BairToAcsp/Routing/TandemNetwork.hpp @@ -0,0 +1,59 @@ +/** + * @file TandemNetwork.hpp + * @brief + * + * Detailed description starts here. + * + * @author Michael Riabzev (), RiabzevMichael@gmail.com + * + * @internal + * Created 11/27/2013 + * Company SCIPR-LAB + * Copyright Copyright (c) 2013, SCIPR-LAB + * ===================================================================================== + */ + +#ifndef TANDEM_NETWORK_HPP__ +#define TANDEM_NETWORK_HPP__ + +#include "RoutingNetwork.hpp" + +#include +#include + +namespace libstark{ +namespace BairToAcsp{ + +/** + * @class TandemNetwork + * @brief A network defined as a concatenation in tandem of some existing networks + * + * This implementation is initialized by a vector of RoutingNetworks, and represents + * a network that is a concatenation of those network, in the given order. + * It is assumed that the last layer of a given network is exactly the + * same as the first layer of the following layer, and they are merged into a single + * layer. This is only an assumption, and if the networks vector does not respect this, + * the result is undefined. For example, if we have the vector \f$ (N_1, N_2, \dots , N_k) \f$ + * and amount of layers in \f$ N_i \f$ is \f$ \ell_i \f$, the amount of layers + * in the concatenation networks is \f$ \ell_1 + (\ell_2 -1) + (\ell_3 - 1) + \cdots + (\ell_k - 1)\f$. + * + * @note It is assumed all the networks in vector has at least 1 layer, and the amount of nodes per layer is common to all of them, otherwise the result is undefined + */ +class TandemNetwork : public RoutingNetwork{ +public: + typedef std::vector> netsVec_t; + + TandemNetwork(netsVec_t&& networks); + layerID_t width()const; + labelID_t height()const; + dataID_t getDataID(const layerID_t& l, const labelID_t& w)const; + void reset(netsVec_t&& networks); + +private: + netsVec_t networks_; +}; + +} //namespace BairToAcsp +} //namespace libstark + +#endif //TANDEM_NETWORK_HPP__ diff --git a/tinyram/gadgetlib/gadgetlib/Makefile b/tinyram/gadgetlib/gadgetlib/Makefile new file mode 100644 index 0000000..494dbe5 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/Makefile @@ -0,0 +1,40 @@ +CC=g++ +CPPFLAGS=-std=c++14 +CFLAGS=-O3 -g -Wall -fmessage-length=0 -fopenmp -mavx -maes -mtune=native + +WHICH := $(shell which which) +MKDIR := $(shell $(WHICH) mkdir) +DIRNAME := $(shell $(WHICH) dirname) + +SRCDIR=. + +CFLAGS+= +CPPFLAGS+= +INCFLAGS=-I./.. -I$(ALGEBRAINC) -I$(FFTINC) -I$(FFTINC)/.. -I$(FFTINC)/src +TARGET=$(BLDDIR)/libgadgetlib.a + +SRCS= \ + common_use.cpp \ + constraint.cpp \ + gadget.cpp \ + infrastructure.cpp \ + protoboard.cpp + +OBJS=$(addprefix $(BLDDIR)/, $(SRCS:.cpp=.o)) + +$(BLDDIR)/%.o: %.cpp +# @echo 'Building file: $@ ($<)' + @$(MKDIR) -p $(shell $(DIRNAME) $@) + $(CC) $(CFLAGS) $(CPPFLAGS) $(INCFLAGS) -c -o "$@" "$<" + +all: $(TARGET) + +clean: + $(RM) -f $(TARGET) $(OBJS) $(DEPS) + +$(TARGET): $(OBJS) +# @echo 'Building target: $@' +# @echo 'Invoking: GCC Archiver' + ar -r "$@" $(OBJS) $(LIBS) +# @echo 'Finished building target: $@' +# @echo ' ' diff --git a/tinyram/gadgetlib/gadgetlib/SHA/sha2.cpp b/tinyram/gadgetlib/gadgetlib/SHA/sha2.cpp new file mode 100644 index 0000000..17708a2 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/SHA/sha2.cpp @@ -0,0 +1,382 @@ +/** @file + ***************************************************************************** + SHA2-specific gadgets. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "sha2.hpp" +#include +#define ROTR(A, i, k) A[((i)+(k)) % 32] + +//using ::std::shared_ptr; +//using ::std::string; +//using ::std::vector; +//using ::std::cout; +//using ::std::cerr; +//using ::std::endl; + +namespace gadgetlib { +namespace sha2 { + +const unsigned long SHA256_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +const unsigned long SHA256_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +void generate_SHA256_IV(ProtoboardPtr pb, ::std::vector& IV) { + for (int i=0; i<8; ++i) { + IV.push_back(VariableArray(32)); + for (int j=0; j<32; ++j) { + int iv_val = (SHA256_H[i] >> j) & 1; + //int iv_val = (SHA256_H[i] >> (31-j)) & 1; + pb->addRank1Constraint(1, IV[i][j], iv_val, "iv"); + pb->val(IV[i][j]) = iv_val; + } + } +} + +MsgSchedule_Gadget::MsgSchedule_Gadget( + ProtoboardPtr pb, const ::std::vector& W) + : Gadget(pb), W_(W) {} + +void MsgSchedule_Gadget::init() { + for(int i=0; i(32) ); + result1_.push_back( ::std::make_shared(32) ); + ssigma0_.push_back( SMALLSIGMA_Gadget::create(pb_, + 7, 18, 3, W_[i+16-15], *result0_[i]) ); + ssigma1_.push_back( SMALLSIGMA_Gadget::create(pb_, + 17, 19, 10, W_[i+16-2], *result1_[i]) ); + add4_.push_back( ADD_Gadget::create(pb_, + W_[i+16-16], *result0_[i], W_[i+16-7], *result1_[i], W_[i+16]) ); + } +} + +void MsgSchedule_Gadget::generateConstraints() { + for (auto& curGadget : ssigma0_) + curGadget->generateConstraints(); + for (auto& curGadget : ssigma1_) + curGadget->generateConstraints(); + for (auto& curGadget : add4_) + curGadget->generateConstraints(); +} + +void MsgSchedule_Gadget::generateWitness() { + for(int i=0; igenerateWitness(); + ssigma1_[i]->generateWitness(); + add4_[i]->generateWitness(); + } +} + +GadgetPtr MsgSchedule_Gadget::create(ProtoboardPtr pb, + const ::std::vector& W) { + addGadgetToTraceback("MsgSchedule_Gadget"); + GadgetPtr pGadget(new MsgSchedule_Gadget(pb, W)); + pGadget->init(); + removeGadgetFromTraceback("MsgSchedule_Gadget"); + return pGadget; +} + +RoundFunc_Gadget::RoundFunc_Gadget(ProtoboardPtr pb, + const VariableArray& a, const VariableArray& b, + const VariableArray& c, const VariableArray& d, + const VariableArray& e, const VariableArray& f, + const VariableArray& g, const VariableArray& h, + const VariableArray& curr_W, const unsigned long curr_K, + const VariableArray& new_a, const VariableArray& new_e) + : Gadget(pb), + a_(a), b_(b), c_(c), d_(d), + e_(e), f_(f), g_(g), h_(h), + curr_W_(curr_W), curr_K_(curr_K), + new_a_(new_a), new_e_(new_e), + s0_(32), s1_(32), ch_(32), maj_(32), add_(32) {} + +void RoundFunc_Gadget::init() { + compute_s0_ = BIGSIGMA_Gadget::create(pb_, + 2, 13, 22, a_, s0_); + compute_s1_ = BIGSIGMA_Gadget::create(pb_, + 6, 11, 25, e_, s1_); + compute_ch_ = MUX_Gadget::create(pb_, + e_, f_, g_, ch_); + compute_maj_ = MAJ_Gadget::create(pb_, + a_, b_, c_, maj_); + compute_add_ = ADD_Gadget::create(pb_, + h_, s1_, ch_, curr_W_, curr_K_, add_); + compute_new_e_ = ADD_Gadget::create(pb_, + d_, add_, new_e_); + compute_new_a_ = ADD_Gadget::create(pb_, + s0_, maj_, add_, new_a_); +} + +void RoundFunc_Gadget::generateConstraints() { + compute_s0_->generateConstraints(); + compute_s1_->generateConstraints(); + compute_ch_->generateConstraints(); + compute_maj_->generateConstraints(); + compute_add_->generateConstraints(); + compute_new_e_->generateConstraints(); + compute_new_a_->generateConstraints(); +} + +void RoundFunc_Gadget::generateWitness() { + compute_s0_->generateWitness(); + compute_s1_->generateWitness(); + compute_ch_->generateWitness(); + compute_maj_->generateWitness(); + compute_add_->generateWitness(); + compute_new_e_->generateWitness(); + compute_new_a_->generateWitness(); +} + +GadgetPtr RoundFunc_Gadget::create(ProtoboardPtr pb, + const VariableArray& a, const VariableArray& b, + const VariableArray& c, const VariableArray& d, + const VariableArray& e, const VariableArray& f, + const VariableArray& g, const VariableArray& h, + const VariableArray& curr_W, const unsigned long curr_K, + const VariableArray& new_a, const VariableArray& new_e) { + addGadgetToTraceback("RoundFunc_Gadget"); + GadgetPtr pGadget(new RoundFunc_Gadget(pb, + a, b, c, d, e, f, g, h, + curr_W, curr_K, new_a, new_e)); + pGadget->init(); + removeGadgetFromTraceback("RoundFunc_Gadget"); + return pGadget; +} + +CompressFunc_Gadget::CompressFunc_Gadget(ProtoboardPtr pb, + const ::std::vector& prev, + const ::std::vector& inp, + const ::std::vector& out) + : Gadget(pb), prev_(prev), inp_(inp), out_(out) {} + +void CompressFunc_Gadget::init() { + ::std::vector W; + for(int i=0; i<16 && igenerateConstraints(); + for (size_t i=0; igenerateConstraints(); + for (size_t i=0; i<8; ++i) + add_funcs_[i]->generateConstraints(); +} + +void CompressFunc_Gadget::generateWitness() { + compute_msgsched_->generateWitness(); + for (size_t i=0; igenerateWitness(); + for (size_t i=0; i<8; ++i) + add_funcs_[i]->generateWitness(); +} + +GadgetPtr CompressFunc_Gadget::create(ProtoboardPtr pb, + const ::std::vector& prev, + const ::std::vector& inp, + const ::std::vector& out) { + addGadgetToTraceback("CompressFunc_Gadget"); + GadgetPtr pGadget(new CompressFunc_Gadget(pb, + prev, inp, out)); + pGadget->init(); + removeGadgetFromTraceback("CompressFunc_Gadget"); + return pGadget; +} + +#ifdef TOY_SHA2_INCLUDE + +ToySHA2_Gadget::ToySHA2_Gadget(ProtoboardPtr pb, + const VariableArray& prev, + const VariableArray& inp, + const VariableArray& out) + : Gadget(pb), prev_(prev), inp_(inp), out_(out) {} + +void ToySHA2_Gadget::init() { + VariableArray word_prev_; + VariableArray word_inp_; + std::vector expand_prev_; + std::vector expand_inp_; + std::vector expand_out_; + + if (prev_.size() < 32) { + word_prev_ = VariableArray(32); + for(int i=0; i<32; ++i) + word_prev_[i] = prev_[i % prev_.size()]; + } + else + word_prev_ = prev_; + for(int i=0; i<8; ++i) + expand_prev_.push_back(word_prev_); + + if (inp_.size() < 32) { + word_inp_ = VariableArray(32); + for(int i=0; i<32; ++i) + word_inp_[i] = inp_[i % inp_.size()]; + } + else + word_inp_ = inp_; + for(int i=0; i<16 && igenerateConstraints(); +} + +void ToySHA2_Gadget::generateWitness() { + sha256_compress_->generateWitness(); +} + +GadgetPtr ToySHA2_Gadget::create(ProtoboardPtr pb, + const VariableArray& prev, + const VariableArray& inp, + const VariableArray& out) { + addGadgetToTraceback("ToySHA2_Gadget"); + GadgetPtr pGadget(new ToySHA2_Gadget(pb, + prev, inp, out)); + pGadget->init(); + removeGadgetFromTraceback("ToySHA2_Gadget"); + return pGadget; +} + +#endif //of TOY_SHA2_INCLUDE + +BIGSIGMA_Gadget::BIGSIGMA_Gadget(ProtoboardPtr pb, + const size_t rot1, const size_t rot2, const size_t rot3, + const VariableArray& input, const VariableArray& result) + : Gadget(pb), input_(input), result_(result) { + for(int i=0; igenerateConstraints(); +} + +void BIGSIGMA_Gadget::generateWitness() { + for (auto& curGadget : xorGadgets_) + curGadget->generateWitness(); +} + +GadgetPtr BIGSIGMA_Gadget::create(ProtoboardPtr pb, + const size_t rot1, const size_t rot2, const size_t rot3, + const VariableArray& input, const VariableArray& result) { + addGadgetToTraceback("BIGSIGMA_Gadget"); + GadgetPtr pGadget(new BIGSIGMA_Gadget(pb, + rot1, rot2, rot3, input, result)); + pGadget->init(); + removeGadgetFromTraceback("BIGSIGMA_Gadget"); + return pGadget; +} + +SMALLSIGMA_Gadget::SMALLSIGMA_Gadget(ProtoboardPtr pb, + const size_t rot1, const size_t rot2, const size_t shift, + const VariableArray& input, const VariableArray& result) + : Gadget(pb), input_(input), result_(result) { + for(int i=0; igenerateConstraints(); +} + +void SMALLSIGMA_Gadget::generateWitness() { + for (auto& curGadget : xorGadgets_) + curGadget->generateWitness(); +} + +GadgetPtr SMALLSIGMA_Gadget::create(ProtoboardPtr pb, + const size_t rot1, const size_t rot2, const size_t shift, + const VariableArray& input, const VariableArray& result) { + addGadgetToTraceback("SMALLSIGMA_Gadget"); + GadgetPtr pGadget(new SMALLSIGMA_Gadget(pb, + rot1, rot2, shift, input, result)); + pGadget->init(); + removeGadgetFromTraceback("SMALLSIGMA_Gadget"); + return pGadget; +} + +} // namespace sha2 +} // namespace gadgetlib diff --git a/tinyram/gadgetlib/gadgetlib/SHA/sha2.hpp b/tinyram/gadgetlib/gadgetlib/SHA/sha2.hpp new file mode 100644 index 0000000..32747a4 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/SHA/sha2.hpp @@ -0,0 +1,172 @@ +/** @file + ***************************************************************************** + SHA2-specific gadgets. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef LIBSNARK_SHA2_INCLUDE_SHA2_GADGET_HPP_ +#define LIBSNARK_SHA2_INCLUDE_SHA2_GADGET_HPP_ + +#include +#define SHA2RND 64 +#define TOY_SHA2_INCLUDE + +namespace gadgetlib { +namespace sha2 { + +void generate_SHA256_IV(ProtoboardPtr pb, ::std::vector& IV); + +class MsgSchedule_Gadget : public Gadget { +private: + MsgSchedule_Gadget(ProtoboardPtr pb, const ::std::vector& W); + virtual void init(); + const ::std::vector W_; + //const ::std::vector& W_; + ::std::vector ssigma0_; + ::std::vector ssigma1_; + ::std::vector add4_; + ::std::vector< ::std::shared_ptr > result0_; + ::std::vector< ::std::shared_ptr > result1_; + DISALLOW_COPY_AND_ASSIGN(MsgSchedule_Gadget); +public: + static GadgetPtr create(ProtoboardPtr pb, const ::std::vector& W); + void generateConstraints(); + void generateWitness(); +}; // class MsgSchedule_Gadget + +class RoundFunc_Gadget : public Gadget { +private: + RoundFunc_Gadget(ProtoboardPtr pb, + const VariableArray& a, const VariableArray& b, + const VariableArray& c, const VariableArray& d, + const VariableArray& e, const VariableArray& f, + const VariableArray& g, const VariableArray& h, + const VariableArray& curr_W, const unsigned long curr_K, + const VariableArray& new_a, const VariableArray& new_e); + virtual void init(); + const VariableArray a_, b_, c_, d_, e_, f_, g_, h_; + const unsigned long curr_K_; + const VariableArray curr_W_, new_a_, new_e_; + const VariableArray s0_, s1_, ch_, maj_, add_; + GadgetPtr compute_s0_, compute_s1_; + GadgetPtr compute_ch_, compute_maj_, compute_add_; + GadgetPtr compute_new_a_, compute_new_e_; + DISALLOW_COPY_AND_ASSIGN(RoundFunc_Gadget); +public: + static GadgetPtr create(ProtoboardPtr pb, + const VariableArray& a, const VariableArray& b, + const VariableArray& c, const VariableArray& d, + const VariableArray& e, const VariableArray& f, + const VariableArray& g, const VariableArray& h, + const VariableArray& curr_W, const unsigned long curr_K, + const VariableArray& new_a, const VariableArray& new_e); + void generateConstraints(); + void generateWitness(); +}; // class RoundFunc_Gadget + +class CompressFunc_Gadget : public Gadget { +private: + CompressFunc_Gadget(ProtoboardPtr pb, + const ::std::vector& prev, + const ::std::vector& inp, + const ::std::vector& out); + virtual void init(); + const ::std::vector prev_; + const ::std::vector inp_; + const ::std::vector out_; + ::std::vector round_a_; + ::std::vector round_b_; + ::std::vector round_c_; + ::std::vector round_d_; + ::std::vector round_e_; + ::std::vector round_f_; + ::std::vector round_g_; + ::std::vector round_h_; + GadgetPtr compute_msgsched_; + ::std::vector round_funcs_; + ::std::vector add_funcs_; + DISALLOW_COPY_AND_ASSIGN(CompressFunc_Gadget); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ::std::vector& prev, + const ::std::vector& inp, + const ::std::vector& out); + void generateConstraints(); + void generateWitness(); +}; + +#ifdef TOY_SHA2_INCLUDE + +class ToySHA2_Gadget : public Gadget { +private: + ToySHA2_Gadget(ProtoboardPtr pb, + const VariableArray& prev, + const VariableArray& inp, + const VariableArray& out); + virtual void init(); + GadgetPtr sha256_compress_; + const VariableArray prev_; + const VariableArray inp_; + const VariableArray out_; + DISALLOW_COPY_AND_ASSIGN(ToySHA2_Gadget); +public: + static GadgetPtr create(ProtoboardPtr pb, + const VariableArray& prev, + const VariableArray& inp, + const VariableArray& out); + void generateConstraints(); + void generateWitness(); +}; + +#endif //of TOY_SHA2_INCLUDE + +class BIGSIGMA_Gadget : public Gadget { +private: + BIGSIGMA_Gadget(ProtoboardPtr pb, + const size_t rot1, + const size_t rot2, + const size_t rot3, + const VariableArray& input, + const VariableArray& result); + virtual void init(); + const VariableArray input_; + const VariableArray result_; + ::std::vector xorGadgets_; + DISALLOW_COPY_AND_ASSIGN(BIGSIGMA_Gadget); +public: + static GadgetPtr create(ProtoboardPtr pb, + const size_t rot1, const size_t rot2, const size_t rot3, + const VariableArray& input, const VariableArray& result); + void generateConstraints(); + void generateWitness(); +}; // class BIGSIGMA_Gadget + +class SMALLSIGMA_Gadget : public Gadget { +private: + SMALLSIGMA_Gadget(ProtoboardPtr pb, + const size_t rot1, + const size_t rot2, + const size_t shift, + const VariableArray& input, + const VariableArray& result); + virtual void init(); + const VariableArray input_; + const VariableArray result_; + ::std::vector xorGadgets_; + DISALLOW_COPY_AND_ASSIGN(SMALLSIGMA_Gadget); +public: + static GadgetPtr create(ProtoboardPtr pb, + const size_t rot1, const size_t rot2, const size_t shift, + const VariableArray& input, const VariableArray& result); + void generateConstraints(); + void generateWitness(); +}; // class SMALLSIGMA_Gadget + +} // namespace sha2 +} // namespace gadgetlib + +#endif // LIBSNARK_SHA2_INCLUDE_SHA2_GADGET_HPP_ diff --git a/tinyram/gadgetlib/gadgetlib/common_use.cpp b/tinyram/gadgetlib/gadgetlib/common_use.cpp new file mode 100644 index 0000000..ac9956f --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/common_use.cpp @@ -0,0 +1,151 @@ +#include +#include +#include +#include + +namespace gadgetlib{ + +using Algebra::FieldElement; +using Algebra::FElem; +using Algebra::xFE; + + //useful for constructing SelectorSum objects + std::vector getPCVars(const Algebra::UnpackedWord& pc){ + std::vector retVal; + for (unsigned int i = 0; i < pc.size(); i++) + retVal.push_back(pc[i]); + return retVal; + } + + + //MachineInstruction::MachineInstruction(const Opcode& opcode, const bool arg2isImmediate, + // const size_t destIdx, const size_t arg1Idx, const size_t arg2IdxOrImmediate) : + // opcode_(opcode), arg2isImmediate_(arg2isImmediate), destIdx_(destIdx), arg1Idx_(arg1Idx), + // arg2IdxOrImmediate_(arg2IdxOrImmediate){} + +FieldElement getGF2E_X() { + return xFE(); +} + +const FieldElement& getGenerator2toTheNthPower(const unsigned int n){ + static std::vector g_2_i = { getGF2E_X() }; + for (unsigned int i = g_2_i.size(); i <= n; ++i) + g_2_i.push_back(Algebra::power(g_2_i[i - 1], 2)); + return g_2_i[n]; +} + +FieldElement getGeneratorPower(const unsigned int n){ + const FieldElement generator = getGF2E_X(); + + FieldElement res = Algebra::one(); + const int expLog = floor(Log2(n)); + for(int i=expLog; i>=0; i--){ + res *= res; + if((n>>i) & 1UL){ + res *= generator; + } + } + + return res; +} + + + +#define tempSelector +Algebra::CircuitPolynomial getSelector(int programLength, int instructionLine, Algebra::UnpackedWord unpakedPC){ + GADGETLIB_ASSERT(unpakedPC.size() >= Log2ceiled(programLength), "Number of unpacked bits used should be at least log length of the program"); + FElem value = (instructionLine & 1U) ? Algebra::one() : Algebra::zero(); +#ifndef tempSelector + + Algebra::CircuitPolynomial selectorPoly(Algebra::one() + value + unpakedPC[0]); + + instructionLine >>= 1U; + int i = 1; + while (instructionLine || i < unpakedPC.size()){ + FElem value = (instructionLine & 1U) ? Algebra::one() : Algebra::zero(); + selectorPoly = selectorPoly * (Algebra::one() + value + unpakedPC[i]); + i++; + instructionLine >>= 1U; + } + return selectorPoly; +#else + std::vector lcVec; + Algebra::LinearCombination lc(Algebra::one() + value + unpakedPC[0]); + lcVec.push_back(lc); + instructionLine >>= 1U; + unsigned int i = 1; + while (instructionLine || i < unpakedPC.size()){ + FElem value = (instructionLine & 1U) ? Algebra::one() : Algebra::zero(); + lcVec.push_back(Algebra::one() + value + unpakedPC[i]); + i++; + instructionLine >>= 1U; + } + return Algebra::CircuitPolynomial(lcVec); + +#endif +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Memory Info ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +MemoryInfo::MemoryInfo(){ + serialNumber_ = 0; + isMemOp_ = 0; + isLoadOp_ = 0; + timestamp_ = Algebra::zero(); + timestampDegree_ = -1; + address_ = Algebra::zero(); + value_ = Algebra::zero(); +} + +MemoryInfo::MemoryInfo(int serialNumber, bool isMemOp, bool isLoadOp, + const FElem& timestamp, int timestampDegree, + const FElem& address, const FElem& value){ + serialNumber_ = serialNumber; + isMemOp_ = isMemOp; + isLoadOp_ = isLoadOp; + timestamp_ = timestamp; + timestampDegree_ = timestampDegree; + address_ = address; + value_ = value; + GADGETLIB_ASSERT(timestamp == getGeneratorPower(timestampDegree_), "g^timestampDegree != timestamp"); +} + +void MemoryInfo::updateTimestamp(FElem timestamp, int timestampDegree){ + GADGETLIB_ASSERT(timestamp == getGeneratorPower(timestampDegree), "g^timestampDegree != timestamp"); + timestamp_ = timestamp; + timestampDegree_ = timestampDegree; +} + +bool sortMemoryInfo(MemoryInfo a, MemoryInfo b){ + if (!a.getIsMemOp() && !b.getIsMemOp()){ + return a.getSerialNumber() < b.getSerialNumber(); + } + else if (a.getIsMemOp() && !b.getIsMemOp()){ + return true; + } + else if (!a.getIsMemOp() && b.getIsMemOp()){ + return false; + } + else{ // MemOp = True -> Sort By Address + int aAddr = mapFieldElementToInteger(0, 16, a.getAddress()); + int bAddr = mapFieldElementToInteger(0, 16, b.getAddress()); + if (aAddr < bAddr){ + return true; + } + else if (bAddr < aAddr){ + return false; + } + else{ // MemOp = True and Same Address -> Sort By TimeStamp + return a.getTimestampDegree() < b.getTimestampDegree(); + } + } +} + + + +} // namespace diff --git a/tinyram/gadgetlib/gadgetlib/common_use.hpp b/tinyram/gadgetlib/gadgetlib/common_use.hpp new file mode 100644 index 0000000..afe96f2 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/common_use.hpp @@ -0,0 +1,127 @@ +#ifndef GADGETLIB3_COMMON_USE_HPP_ +#define GADGETLIB3_COMMON_USE_HPP_ + +#include +#include +#include +#include +#include +#include +#include + + +namespace gadgetlib{ + Algebra::FieldElement getGF2E_X(); + const Algebra::FieldElement& getGenerator2toTheNthPower(const unsigned int n); + Algebra::FieldElement getGeneratorPower(const unsigned int n); + +enum class Opcode : int { + MEMORY = -2, + NONE = -1, + AND = 0, + OR = 1, + XOR = 2, + NOT = 3, + ADD = 4, + SUB = 5, + MULL = 6, + UMULH = 7, + SMULH = 8, + UDIV = 9, + UMOD = 10, + SHL = 11, + SHR = 12, + SHAR = 13, + + CMPE = 14, + CMPA = 15, + CMPAE = 16, + CMPG = 17, + CMPGE = 18, + + MOV = 19, + CMOV = 20, + JMP = 21, + + CJMP = 22, + CNJMP = 23, + + RESERVED_OPCODE_24 = 24, + RESERVED_OPCODE_25 = 25, + + STOREB = 26, + LOADB = 27, + STOREW = 28, + LOADW = 29, + READ = 30, + ANSWER = 31, + NUM_OPCODES = 32 +}; // enum Opcode + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/**************************** ****************************/ +/**************************** Selector Functions ****************************/ +/**************************** ****************************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +//useful for constructing SelectorSum objects +std::vector getPCVars(const Algebra::UnpackedWord& pc); + + +// We need the unpakcedPC be at least the log(programLength). +// The polynomial is Product ( +Algebra::CircuitPolynomial getSelector(int programLength, int instructionLine, Algebra::UnpackedWord unpakedPC); + + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Memory Info ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class MemoryInfo{ +private: + int serialNumber_; + bool isMemOp_; + bool isLoadOp_; + Algebra::FElem timestamp_; + int timestampDegree_; + Algebra::FElem address_; + Algebra::FElem value_; +public: + MemoryInfo(); + MemoryInfo(int serialNumber, bool isMemOp, bool isLoadOp, + const Algebra::FElem& timestamp, int timestampDegree, + const Algebra::FElem& address, const Algebra::FElem& value); + int getSerialNumber() const{ return serialNumber_; } + bool getIsMemOp() const{ return isMemOp_; } + bool getIsLoadOp() const { return isLoadOp_; } + Algebra::FElem getTimestamp() const{ return timestamp_; } + int getTimestampDegree() const{ return timestampDegree_; } + Algebra::FElem getAddress() const { return address_; } + Algebra::FElem getValue() const{ return value_; } + void updateIsMemOp(bool isMemOp){ isMemOp_ = isMemOp; } + void updateIsLoadOp(bool isLoadOp){ isLoadOp_ = isLoadOp; } + void updateAddress(const Algebra::FElem& address){ address_ = address; } + void updateSerialNumber(int serialNumber){ serialNumber_ = serialNumber; } + void updateValue(const Algebra::FElem& value){ value_ = value; } + void updateTimestamp(Algebra::FElem timestamp, int timestampDegree); +}; + +inline bool operator==(const MemoryInfo& a, const MemoryInfo& b){ + return (a.getSerialNumber() == b.getSerialNumber() && a.getIsMemOp() == b.getIsMemOp() && a.getIsLoadOp() == b.getIsLoadOp() && + a.getTimestamp() == b.getTimestamp() && a.getAddress() == b.getAddress() && a.getValue() == b.getValue()); +} + +bool sortMemoryInfo(MemoryInfo a, MemoryInfo b); + +} // namespace + + +#endif // GADGETLIB3_COMMON_USE_HPP_ diff --git a/tinyram/gadgetlib/gadgetlib/constraint.cpp b/tinyram/gadgetlib/gadgetlib/constraint.cpp new file mode 100644 index 0000000..6b4cfb8 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/constraint.cpp @@ -0,0 +1,83 @@ +/** @file + ***************************************************************************** + Implementation of the Constraint class. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include "constraint.hpp" + +using ::std::string; +using ::std::vector; +using ::std::set; +using ::std::cout; +using ::std::cerr; +using ::std::endl; +using ::std::shared_ptr; + +namespace gadgetlib { + +typedef Algebra::CircuitPolynomial Polynomial; + + +bool Constraint::isSatisfied(const Algebra::VariableAssignment& assignment, + const PrintOptions& printOnFail){ + + bool aEval = constraint_.isSatisfied(assignment); + if (aEval == false){ + if (printOnFail == PrintOptions::DBG_PRINT_IF_NOT_SATISFIED) { + cerr << "Constraint named:" << name_ << "not satisfied" << endl; + cerr << "Expecting: " << aEval << endl; + cerr << "Variable assignments are:" << endl; + const Algebra::Variable::set varSet = getUsedVariables(); + for (const Algebra::Variable& var : varSet) { + cerr << var.name() << ": " << assignment.at(var).asString() << endl; + } + } + } + return true; +} + + +void ConstraintSystem::addConstraint(Polynomial c, std::string name) { + Constraint newConstraint(c, name); + constraintsPtrs_.emplace_back(newConstraint); +} + +bool ConstraintSystem::isSatisfied(const Algebra::VariableAssignment& assignment, + const PrintOptions& printOnFail) const { + for(size_t i = 0; i < constraintsPtrs_.size(); ++i) { + if (!(constraintsPtrs_[i].constraint().isSatisfied(assignment))){ + #ifdef _DEBUG + std::cout << "Wrong: " << constraintsPtrs_[i].asString() << std::endl; +// std::cout << constraintsPtrs_[i].constraint().eval(assignment) << std::endl; +// std::cout << "Constraint: " << constraintsPtrs_[i].constraint().asString() << std::endl; + #endif + return false; + } + } + return true; +} + +Algebra::Variable::set ConstraintSystem::getUsedVariables() const { + Algebra::Variable::set retSet; + for(auto& pConstraint : constraintsPtrs_) { + const Algebra::Variable::set curSet = pConstraint.getUsedVariables(); + retSet.insert(curSet.begin(), curSet.end()); + } + return retSet; +} + +void ConstraintSystem::setNewIndices(std::map& old2New) { + for (auto& pConstraint : constraintsPtrs_){ + pConstraint.setNewIndices(old2New); + } +} +} // namespace gadgetlib diff --git a/tinyram/gadgetlib/gadgetlib/constraint.hpp b/tinyram/gadgetlib/gadgetlib/constraint.hpp new file mode 100644 index 0000000..9510a92 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/constraint.hpp @@ -0,0 +1,107 @@ +/** @file + ***************************************************************************** + Declaration of the Constraint class. + + A constraint is an algebraic equation which can be either satisfied by an assignment, + (the equation is true with that assignment) or unsatisfied. For instance the rank-1 + constraint (X * Y = 15) is satisfied by {X=5 Y=3} or {X=3 Y=5} + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef GADGETLIB3_CONSTRAINT_HPP_ +#define GADGETLIB3_CONSTRAINT_HPP_ + +#include +#include +#include +#include +#include +#include +#include + + + +namespace gadgetlib { + +using Algebra::FieldElement; +typedef Algebra::CircuitPolynomial Polynomial; + +enum class PrintOptions { + DBG_PRINT_IF_NOT_SATISFIED, + DBG_PRINT_IF_TRUE, + DBG_PRINT_IF_FALSE, + NO_DBG_PRINT +}; + +class Constraint{ + Polynomial constraint_; + std::string name_; + +public: + Constraint(Polynomial polynomial, std::string name = "") :constraint_(polynomial), name_(name){}; + std::string asString() const { return name_; } + Algebra::Variable::set getUsedVariables() const { return constraint_.getUsedVariables(); } + Polynomial constraint() const { return constraint_; } + bool isSatisfied(const Algebra::VariableAssignment& assignment, + const PrintOptions& printOnFail = PrintOptions::NO_DBG_PRINT); + void setNewIndices(std::map& old2New) { constraint_.setNewIndices(old2New); } + void getConsts(map& FElemMap) const { return constraint_.getConsts(FElemMap); } +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class ConstraintSystem ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ConstraintSystem { +protected: + //typedef ::std::shared_ptr ConstraintPtr; + ::std::vector constraintsPtrs_; + +public: + ConstraintSystem() : constraintsPtrs_() {}; + + /** + Checks if all constraints are satisfied by an assignment. + @param[in] assignment - An assignment of field elements for each variable. + @param[in] printOnFail - when set to true, an unsatisfied constraint will print to stderr + information explaining why it is not satisfied. + @returns true if constraint is satisfied by the assignment + **/ + bool isSatisfied(const Algebra::VariableAssignment& assignment, + const PrintOptions& printOnFail = PrintOptions::NO_DBG_PRINT) const; + + void addConstraint(const Polynomial c, std::string name = ""); + Algebra::Variable::set getUsedVariables() const; + + ::std::vector getConstraints(){ + return constraintsPtrs_; + } + + typedef std::vector< std::unique_ptr > PolyPtrSet; + /// Required for interfacing with BREX. Should be optimized in the future + PolyPtrSet getConstraintPolynomials() const { + PolyPtrSet retset; + for(const Constraint& pConstraint : constraintsPtrs_) { + retset.push_back(pConstraint.constraint().clone()); + } + return retset; + } + + size_t getNumberOfConstraints() { return constraintsPtrs_.size(); } + void setNewIndices(std::map& old2New); + friend class GadgetLibAdapter; +}; // class ConstraintSystem + +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + +} // namespace gadgetlib + +#endif //GADGETLIB3_CONSTRAINT_HPP_ diff --git a/tinyram/gadgetlib/gadgetlib/examples/tutorial.cpp b/tinyram/gadgetlib/gadgetlib/examples/tutorial.cpp new file mode 100644 index 0000000..a0fa0e0 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/examples/tutorial.cpp @@ -0,0 +1,506 @@ +/** @file +******************************************************************************** +Tutorial and usage examples of the gadgetlib2 library and ppzkSNARK integration. +This file is meant to be read top-down as a tutorial for gadget writing. + ******************************************************************************** + Tutorial and usage examples of the gadgetlib2 library. + ******************************************************************************** + * @authors Eli Ben-Sasson, Alessandro Chiesa, Daniel Genkin, + * Shaul Kfir, Eran Tromer, Madars Virza. + * This file is part of libsnark, developed by SCIPR Lab . + * @copyright MIT license (see LICENSE file) + *******************************************************************************/ + +#include +#include +#include "r1cs_ppzksnark/examples/r1cs_examples.hpp" +#include "gadgetlib2/examples/simple_example.hpp" +#include "r1cs_ppzksnark/examples/run_r1cs_ppzksnark.hpp" + +namespace gadgetExamples { + +using namespace gadgetlib2; + +/* + This test gives the first example of a construction of a constraint system. We use the terms + 'Constraint System' and 'Circuit' interchangeably rather loosly. It is easy to + visualize a circuit with inputs and outputs. Each gate imposes some logic on the inputs and + output wires. For instance, AND(inp1, inp2) will impose the 'constraint' (inp1 & inp2 = out) + Thus, we can also think of this circuit as a system of constraints. Each gate is a mathematical + constraint and each wire is a variable. In the AND example over a boolean field {0,1} we would + write the constraint as (inp1 * inp2 == out). This constraint is 'satisfied' relative to an + assignment if we assign values to {inp1, inp2, out} such that the constraint evaluates to TRUE. + All following examples will be either field agnostic or of a specific form of prime fields: + (1) Field agnostic case: In these examples we create high level circuits by using lower level + circuits. This way we can ignore the specifics of a field and assume the lower level takes + care of this. If we must explicitly write constraints in these circuits, they will always + be very basic constraints which are defined over every field (e.g. x + y = 0). + (2) All field specific examples in this library are for a prime characteristic field with the + special form of 'quadratic rank 1 polynomials', or R1P. This is the only form used with the + current implementaition of SNARKs. The form for these constraints is + (Linear Combination) * (Linear Combination) == (Linear Combination). + The library has been designed to allow future addition of other characteristics/forms in + the future by implementing only low level circuits for these forms. +*/ +TEST(Examples, ProtoboardUsage) { + // Initialize prime field parameters. This is always needed for R1P. + initPublicParamsFromDefaultPp(); + // The protoboard is the 'memory manager' which holds all constraints (when creating the + // verifying circuit) and variable assignments (when creating the proof witness). We specify + // the type as R1P, this can be augmented in the future to allow for BOOLEAN or GF2_EXTENSION + // fields in the future. + ProtoboardPtr pb = Protoboard::create(R1P); + // We now create 3 input variables and one output + VariableArray input(3, "input"); + Variable output("output"); + // We can now add some constraints. The string is for debuging purposes and can be a textual + // description of the constraint + pb->addRank1Constraint(input[0], 5 + input[2], output, + "Constraint 1: input[0] * (5 + input[2]) == output"); + // The second form addUnaryConstraint(LinearCombination) means (LinearCombination == 0). + pb->addUnaryConstraint(input[1] - output, + "Constraint 2: input[1] - output == 0"); + // Notice this could also have been written: + // pb->addRank1Constraint(1, input[1] - input[2], 0, ""); + // + // For fields with more general forms, once implemented, we could use + // addGeneralConstraint(Polynomial1, Polynomial2, string) which translates to the constraint + // (Polynomial1 == Polynomial2). Example: + // pb->addGeneralConstraint(input[0] * (3 + input[1]) * input[2], output + 5, + // "input[0] * (3 + input[1]) * input[2] == output + 5"); + // + // Now we can assign values to the variables and see if the constraints are satisfied. + // Later, when we will run a SNARK (or any other proof system), the constraints will be + // used by the verifier, and the assigned values will be used by the prover. + // Notice the protoboard stores the assignment values. + pb->val(input[0]) = pb->val(input[1]) = pb->val(input[2]) = pb->val(output) = 42; + EXPECT_FALSE(pb->isSatisfied()); + // The constraint system is not satisfied. Now lets try values which satisfy the two equations + // above: + pb->val(input[0]) = 1; + pb->val(input[1]) = pb->val(output) = 42; // input[1] - output == 0 + pb->val(input[2]) = 37; // 1 * (5 + 37) == 42 + EXPECT_TRUE(pb->isSatisfied()); +} + +/* + In the above example we explicitly wrote all constraints and assignments. + + In this example we will construct a very simple gadget, one that implements a NAND gate. This + gadget is field-agnostic as it only uses lower level gadgets and the field elments '0' and '1'. + + Gadgets are the framework which allow us to delegate construction of sophisticated circuitry + to lower levels. Each gadget can construct a constraint system or a witness or both, by + defining constraints and assignments as well as by utilizing sub-gadgets. +*/ + +class NAND_Gadget : public Gadget { +public: + // This is a convention we use to always create gadgets as if from a factory class. This will + // be needed later for gadgets which have different implementaions in different fields. + static GadgetPtr create(ProtoboardPtr pb, + const FlagVariableArray& inputs, + const FlagVariable& output); + // generateConstraints() is the method which creates all constraints on the protoboard + void generateConstraints(); + // generateWitness() is the method which generates the witness by assigning a valid value to + // each wire in the circuit (variable) and putting this on the protoboard + void generateWitness(); +private: + // constructor is private in order to stick to the convention that gadgets are created using a + // create() method. This may not make sense now, but when dealing with non-field agnostic + // gadgets it is very convenient to have a factory class with this convention. + // Notice the protoboard. This can be thought of as a 'memory manager' which holds the circuit + // as the constraints are being built, and the 'wire values' as the witness is being built + NAND_Gadget(ProtoboardPtr pb, const FlagVariableArray& inputs, const FlagVariable& output); + // init() does any non trivial work which we don't want in the constructor. This is where we + // will 'wire' the sub-gadgets into the circuit. Each sub-gadget can be thought of as a + // circuit gate with some specific functionality. + void init(); + // we want every gadget to be explicitly constructed + DISALLOW_COPY_AND_ASSIGN(NAND_Gadget); + + // This is an internal gadget. Once a gadget is created it can be used as a black box gate. We + // will initialize this pointer to be an AND_Gadget in the init() method. + GadgetPtr andGadget_; + // These are internal variables used by the class. They will always include the variables from + // the constructor, but can include many more as well. Notice that almost always the variables + // can be declared 'const', as these are local copies of formal variables, and do not change + // over the span of the class' lifetime. + const VariableArray inputs_; + const FlagVariable output_; + const FlagVariable andResult_; +}; + +// IMPLEMENTATION +// Most constructors are trivial and only initialize and assert values. +NAND_Gadget::NAND_Gadget(ProtoboardPtr pb, + const FlagVariableArray& inputs, + const FlagVariable& output) + : Gadget(pb), inputs_(inputs), output_(output), andResult_("andResult") {} + +void NAND_Gadget::init() { + // we 'wire' the AND gate. + andGadget_ = AND_Gadget::create(pb_, inputs_, andResult_); +} + +// The create() method will usually look like this, for field-agnostic gadgets: +GadgetPtr NAND_Gadget::create(ProtoboardPtr pb, + const FlagVariableArray& inputs, + const FlagVariable& output) { + GadgetPtr pGadget(new NAND_Gadget(pb, inputs, output)); + pGadget->init(); + return pGadget; +} + +void NAND_Gadget::generateConstraints() { + // we will invoke the AND gate constraint generator + andGadget_->generateConstraints(); + // and add our out negation constraint in order to make this a NAND gate + addRank1Constraint(1, 1 - andResult_, output_, "1 * (1 - andResult) = output"); + // Another way to write the same constraint is: + // addUnaryConstraint(1 - andResult_ - output_, "1 - andResult == output"); + // + // At first look, it would seem that this is enough. However, the AND_Gadget expects all of its + // inputs to be boolean, a dishonest prover could put non-boolean inputs, so we must check this + // here. Notice 'FlagVariable' means a variable which we intend to hold only '0' or '1', but + // this is just a convention (it is a typedef for Variable) and we must enforce it. + // Look into the internals of the R1P implementation of AND_Gadget and see that + // {2, 1, 0} as inputs with {1} as output would satisfy all constraints, even though this is + // clearly not our intent! + for (const auto& input : inputs_) { + enforceBooleanity(input); // This adds a constraint of the form: input * (1 - input) == 0 + } +} + +void NAND_Gadget::generateWitness() { + // First we can assert that all input values are indeed boolean. The purpose of this assertion + // is simply to print a clear error message, it is not security critical. + // Notice the method val() which returns a reference to the current assignment for a variable + for (const auto& input : inputs_) { + GADGETLIB_ASSERT(val(input) == 0 || val(input) == 1, "NAND input is not boolean"); + } + // we will invoke the AND gate witness generator, this will set andResult_ correctly + andGadget_->generateWitness(); + // and now we set the value of output_ + val(output_) = 1 - val(andResult_); + // notice the use of 'val()' to tell the protoboard to assign this new value to the + // variable 'output_'. The variable itself is only a formal variable and never changes. +} + +// And now for a test which will exemplify the usage: +TEST(Examples, NAND_Gadget) { + // initialize the field + initPublicParamsFromDefaultPp(); + // create a protoboard for a system of rank 1 constraints over a prime field. + ProtoboardPtr pb = Protoboard::create(R1P); + // create 5 variables inputs[0]...iputs[4]. The string "inputs" is used for debug messages + FlagVariableArray inputs(5, "inputs"); + FlagVariable output("output"); + GadgetPtr nandGadget = NAND_Gadget::create(pb, inputs, output); + // now we can generate a constraint system (or circuit) + nandGadget->generateConstraints(); + // if we try to evaluate the circuit now, an exception will be thrown, because we will + // be attempting to evaluate unasigned variables. + EXPECT_ANY_THROW(pb->isSatisfied()); + // so lets assign the input variables for NAND and try again after creating the witness + for (const auto& input : inputs) { + pb->val(input) = 1; + } + nandGadget->generateWitness(); + EXPECT_TRUE(pb->isSatisfied()); + EXPECT_TRUE(pb->val(output) == 0); + // now lets try to ruin something and see what happens + pb->val(inputs[2]) = 0; + EXPECT_FALSE(pb->isSatisfied()); + // now let try to cheat. If we hadn't enforced booleanity, this would have worked! + pb->val(inputs[1]) = 2; + EXPECT_FALSE(pb->isSatisfied()); + // now lets reset inputs[1] to a valid value + pb->val(inputs[1]) = 1; + // before, we set both the inputs and the output. Notice the output is still set to '0' + EXPECT_TRUE(pb->val(output) == 0); + // Now we will let the gadget compute the result using generateWitness() and see what happens + nandGadget->generateWitness(); + EXPECT_TRUE(pb->val(output) == 1); + EXPECT_TRUE(pb->isSatisfied()); +} + +/* + Another example showing the use of DualVariable. A DualVariable is a variable which holds both + a bitwise representation of a word and a packed representation (e.g. both the packed value {42} + and the unpacked value {1,0,1,0,1,0}). If the word is short enough + (for example any integer smaller than the prime characteristic) then the packed representation + will be stored in 1 field element. 'word' in this context means a set of bits, it is a + convention which means we expect some semantic ability to decompose the packed value into its + bits. + The use of DualVariables is for efficiency reasons. More on this at the end of this example. + In this example we will construct a gadget which recieves as input a packed integer value + called 'hash', and a 'difficulty' level in bits, and constructs a circuit validating that the + first 'difficulty' bits of 'hash' are '0'. For simplicity we will assume 'hash' is always 64 + bits long. +*/ + +class HashDifficultyEnforcer_Gadget : public Gadget { +public: + static GadgetPtr create(ProtoboardPtr pb, + const MultiPackedWord& hashValue, + const size_t difficultyBits); + void generateConstraints(); + void generateWitness(); +private: + const size_t hashSizeInBits_; + const size_t difficultyBits_; + DualWord hashValue_; + // This GadgetPtr will be a gadget to unpack hashValue_ from packed representation to bit + // representation. Recall 'DualWord' holds both values, but only the packed version will be + // recieved as input to the constructor. + GadgetPtr hashValueUnpacker_; + + HashDifficultyEnforcer_Gadget(ProtoboardPtr pb, + const MultiPackedWord& hashValue, + const size_t difficultyBits); + void init(); + DISALLOW_COPY_AND_ASSIGN(HashDifficultyEnforcer_Gadget); +}; + +// IMPLEMENTATION +HashDifficultyEnforcer_Gadget::HashDifficultyEnforcer_Gadget(ProtoboardPtr pb, + const MultiPackedWord& hashValue, + const size_t difficultyBits) + : Gadget(pb), hashSizeInBits_(64), difficultyBits_(difficultyBits), + hashValue_(hashValue, UnpackedWord(64, "hashValue_u")) +{ +} + +void HashDifficultyEnforcer_Gadget::init() { + // because we are using a prime field with large characteristic, we can assume a 64 bit value + // fits in the first element of a multipacked variable. + GADGETLIB_ASSERT(hashValue_.multipacked().size() == 1, "multipacked word size too large"); + // A DualWord_Gadget's constraints assert that the unpacked and packed values represent the + // same integer element. The generateWitnes() method has two modes, one for packing (taking the + // bit representation as input) and one for unpacking (creating the bit representation from + // the packed representation) + hashValueUnpacker_ = DualWord_Gadget::create(pb_, hashValue_, PackingMode::UNPACK); +} + +GadgetPtr HashDifficultyEnforcer_Gadget::create(ProtoboardPtr pb, + const MultiPackedWord& hashValue, + const size_t difficultyBits) { + GadgetPtr pGadget(new HashDifficultyEnforcer_Gadget(pb, hashValue, difficultyBits)); + pGadget->init(); + return pGadget; +} + +void HashDifficultyEnforcer_Gadget::generateConstraints() { + // enforce that both representations are equal + hashValueUnpacker_->generateConstraints(); + // add constraints asserting that the first 'difficultyBits' bits of 'hashValue' equal 0. Note + // endianness, unpacked()[0] is LSB and unpacked()[63] is MSB + for (size_t i = 0; i < difficultyBits_; ++i) { + addUnaryConstraint(hashValue_.unpacked()[63 - i], GADGETLIB2_FMT("hashValue[%u] == 0", 63 - i)); + } +} + +void HashDifficultyEnforcer_Gadget::generateWitness() { + // Take the packed representation and unpack to bits. + hashValueUnpacker_->generateWitness(); + // In a real setting we would add an assertion that the value will indeed satisfy the + // difficulty constraint, and notify the user with an error otherwise. As this is a tutorial, + // we'll let invalid values pass through so that we can see how isSatisfied() returns false. +} + +// Remember we pointed out that DualVariables are used for efficiency reasons. Now is the time to +// elaborate on this. As you've seen, we needed a bit representation in order to check the first +// bits of hashValue. But hashValue may be used in many other places, for instance we may want to +// check equality with another value. Checking equality on a packed representation will 'cost' us +// 1 constraint, while checking equality on the unpacked value will 'cost' us 64 constraints. This +// translates heavily into proof construction time and memory in the ppzkSNARK proof system. + +TEST(Examples, HashDifficultyEnforcer_Gadget) { + initPublicParamsFromDefaultPp(); + auto pb = Protoboard::create(R1P); + const MultiPackedWord hashValue(64, R1P, "hashValue"); + const size_t difficulty = 10; + auto difficultyEnforcer = HashDifficultyEnforcer_Gadget::create(pb, hashValue, difficulty); + difficultyEnforcer->generateConstraints(); + // constraints are created but no assignment yet. Will throw error on evaluation + EXPECT_ANY_THROW(pb->isSatisfied()); + pb->val(hashValue[0]) = 42; + difficultyEnforcer->generateWitness(); + // First 10 bits of 42 (when represented as a 64 bit number) are '0' so this should work + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(hashValue[0]) = 1000000000000000000; + // This is a value > 2^54 so we expect constraint system not to be satisfied. + difficultyEnforcer->generateWitness(); // This would have failed had we put an assertion + EXPECT_FALSE(pb->isSatisfied()); +} + + +/* + In this exampe we will construct a gadget which builds a circuit for proof (witness) and + validation (constraints) that a bitcoin transaction's sum of inputs equals the the sum of + outputs + miners fee. Construction of the proof will include finding the miners' + fee. This fee can be thought of as an output of the circuit. + + This is a field specific gadget, as we will use the '+' operator freely. The addition + operation works as expected over integers while in prime characteristic fields but not so in + extension fields. If you are not familiar with extension fields, don't worry about it. Simply + be aware that + and * behave differently in different fields and don't necessarily give the + integer values you would expect. + + The library design supports multiple field constructs due to different applied use cases. Some + cryptogragraphic applications may need extension fields while others may need prime fields + but with constraints which are not rank-1 and yet others may need boolean circuits. The library + was designed so that high level gadgets can be reused by implementing only the low level for + a new field or constraint structure. + + Later we will supply a recipe for creation of such field specfic gadgets with agnostic + interfaces. We use a few conventions here in order to ease the process by using macros. +*/ + + +// This is a macro which creates an interface class for all field specific derived gadgets +// Convention is: class {GadgetName}_GadgetBase +CREATE_GADGET_BASE_CLASS(VerifyTransactionAmounts_GadgetBase); + +// Notice the multiple inheritance. We must specify the interface as well as the field specific +// base gadget. This is what allows the factory class to decide at compile time which field +// specific class to instantiate for every protoboard. See design notes in "gadget.hpp" +// Convention is: class {FieldType}_{GadgetName}_Gadget +class R1P_VerifyTransactionAmounts_Gadget : public VerifyTransactionAmounts_GadgetBase, + public R1P_Gadget { +public: + void generateConstraints(); + void generateWitness(); + + // We give the factory class friend access in order to instantiate via private constructor. + friend class VerifyTransactionAmounts_Gadget; +private: + R1P_VerifyTransactionAmounts_Gadget(ProtoboardPtr pb, + const VariableArray& txInputAmounts, + const VariableArray& txOutputAmounts, + const Variable& minersFee); + void init(); + + const VariableArray txInputAmounts_; + const VariableArray txOutputAmounts_; + const Variable minersFee_; + + DISALLOW_COPY_AND_ASSIGN(R1P_VerifyTransactionAmounts_Gadget); +}; + +// create factory class using CREATE_GADGET_FACTORY_CLASS_XX macro (substitute XX with the number +// of arguments to the constructor, excluding the protoboard). Sometimes we want multiple +// constructors, see AND_Gadget for example. In this case we will have to manually write the +// factory class. +CREATE_GADGET_FACTORY_CLASS_3(VerifyTransactionAmounts_Gadget, + VariableArray, txInputAmounts, + VariableArray, txOutputAmounts, + Variable, minersFee); + +// IMPLEMENTATION + +// Destructor for the Base class +VerifyTransactionAmounts_GadgetBase::~VerifyTransactionAmounts_GadgetBase() {} + +void R1P_VerifyTransactionAmounts_Gadget::generateConstraints() { + addUnaryConstraint(sum(txInputAmounts_) - sum(txOutputAmounts_) - minersFee_, + "sum(txInputAmounts) == sum(txOutputAmounts) + minersFee"); + // It would seem this is enough, but an adversary could cause an overflow of one side of the + // equation over the field modulus. In fact, for every input/output sum we will always find a + // miners' fee which will satisfy this constraint! + // It is left as an excercise for the reader to implement additional constraints (and witness) + // to check that each of the amounts (inputs, outputs, fee) are between 0 and 21,000,000 * 1E8 + // satoshis. Combine this with a maximum amount of inputs/outputs to disallow field overflow. + // + // Hint: use Comparison_Gadget to create a gadget which compares a variable's assigned value + // to a constant. Use a vector of these new gadgets to check each amount. + // Don't forget to: + // (1) Wire these gadgets in init() + // (2) Invoke the gadgets' constraints in generateConstraints() + // (3) Invoke the gadgets' witnesses in generateWitness() +} + +void R1P_VerifyTransactionAmounts_Gadget::generateWitness() { + FElem sumInputs = 0; + FElem sumOutputs = 0; + for (const auto& inputAmount : txInputAmounts_) { + sumInputs += val(inputAmount); + } + for (const auto& outputAmount : txOutputAmounts_) { + sumOutputs += val(outputAmount); + } + val(minersFee_) = sumInputs - sumOutputs; +} + +R1P_VerifyTransactionAmounts_Gadget::R1P_VerifyTransactionAmounts_Gadget( + ProtoboardPtr pb, + const VariableArray& txInputAmounts, + const VariableArray& txOutputAmounts, + const Variable& minersFee) + // Notice we must initialize 3 base classes (diamond inheritance): + : Gadget(pb), VerifyTransactionAmounts_GadgetBase(pb), R1P_Gadget(pb), + txInputAmounts_(txInputAmounts), txOutputAmounts_(txOutputAmounts), + minersFee_(minersFee) {} + +void R1P_VerifyTransactionAmounts_Gadget::init() {} + +/* + As promised, recipe for creating field specific gadgets with agnostic interfaces: + + (1) Create the Base class using macro: + CREATE_GADGET_BASE_CLASS({GadgetName}_GadgetBase); + (2) Create the destructor for the base class: + {GadgetName_Gadget}Base::~{GadgetName}_GadgetBase() {} + (3) Create any field specific gadgets with multiple inheritance: + class {FieldType}_{GadgetName}_Gadget : public {GadgetName}_GadgetBase, + public {FieldType_Gadget} + Notice all arguments to the constructors must be const& in order to use the factory class + macro. Constructor arguments must be the same for all field specific implementations. + (4) Give the factory class {GadgetName}_Gadget public friend access to the field specific + classes. + (5) Create the factory class using the macro: + CREATE_GADGET_FACTORY_CLASS_XX({GadgetName}_Gadget, type1, input1, type2, input2, ... , + typeXX, inputXX); +*/ + +TEST(Examples, R1P_VerifyTransactionAmounts_Gadget) { + initPublicParamsFromDefaultPp(); + auto pb = Protoboard::create(R1P); + const VariableArray inputAmounts(2, "inputAmounts"); + const VariableArray outputAmounts(3, "outputAmounts"); + const Variable minersFee("minersFee"); + auto verifyTx = VerifyTransactionAmounts_Gadget::create(pb, inputAmounts, outputAmounts, + minersFee); + verifyTx->generateConstraints(); + pb->val(inputAmounts[0]) = pb->val(inputAmounts[1]) = 2; + pb->val(outputAmounts[0]) = pb->val(outputAmounts[1]) = pb->val(outputAmounts[2]) = 1; + verifyTx->generateWitness(); + EXPECT_TRUE(pb->isSatisfied()); + EXPECT_EQ(pb->val(minersFee), 1); + pb->val(minersFee) = 3; + EXPECT_FALSE(pb->isSatisfied()); +} + +/* + Below is an example of integrating gadgetlib2 constructed constraint systems with the + ppzkSNARK. +*/ + +TEST(gadgetLib2,Integration) { + initPublicParamsFromDefaultPp(); + // Create an example constraint system and translate to libsnark format + const libsnark::r1cs_example > example = libsnark::gen_r1cs_example_from_gadgetlib2_protoboard(100); + const bool test_serialization = false; + // Run ppzksnark. Jump into function for breakdown + const bool bit = libsnark::run_r1cs_ppzksnark(example, test_serialization); + EXPECT_TRUE(bit); +}; + +} // namespace gadgetExamples + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tinyram/gadgetlib/gadgetlib/gadget.cpp b/tinyram/gadgetlib/gadgetlib/gadget.cpp new file mode 100644 index 0000000..0c7d59d --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/gadget.cpp @@ -0,0 +1,616 @@ +/** @file + ***************************************************************************** + Declarations of the interfaces and basic gadgets for R1P (Rank 1 prime characteristic) + constraint systems. + + See details in gadget.hpp . + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::std::shared_ptr; +using ::std::string; +using ::std::vector; +using ::std::cout; +using ::std::cerr; +using ::std::endl; + + +namespace gadgetlib { + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Gadget Interfaces ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +/***********************************/ +/*** Gadget ***/ +/***********************************/ + +Gadget::Gadget(ProtoboardPtr pb) : pb_(pb) { + GADGETLIB_ASSERT(pb != NULL, "Attempted to create gadget with uninitialized Protoboard."); +} + +void Gadget::generateWitness() { + GADGETLIB_FATAL("Attempted to generate witness for an incomplete Gadget type."); +} + +void Gadget::addGeneralConstraint(const Algebra::CircuitPolynomial& polynomial, + const ::std::string& name, Opcode opcode){ + pb_->addGeneralConstraint(polynomial, name, opcode); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Gadget CompressionPacking_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +CompressionPacking_Gadget::CompressionPacking_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::Variable& packed, + PackingMode packingMode, + Opcode opcode) : + Gadget(pb), + packingMode_(packingMode), + unpacked_(unpacked), + packed_(packed), + opcode_(opcode) +{}; + +GadgetPtr CompressionPacking_Gadget::create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::Variable& packed, + PackingMode packingMode, + Opcode opcode){ + GadgetPtr pGadget(new CompressionPacking_Gadget(pb,unpacked,packed,packingMode,opcode)); + pGadget->init(); + return pGadget; +} + +void CompressionPacking_Gadget::init(){} + +//we either add the constraints that the unpacking is boolean (In UNPACK mode this gadget is really just a booleanity check), +//and in pack mode we only check that +void CompressionPacking_Gadget::generateConstraints(){ + const int numBits = unpacked_.size(); + const int bitsPerFieldElem = Algebra::ExtensionDegree; + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + Algebra::LinearCombination packedPolyLC; + for (int i = 0; i < bitsPerFieldElem && i < numBits; ++i) { + packedPolyLC = packedPolyLC + x_i * unpacked_[i]; + x_i *= x; + if (packingMode_ == PackingMode::UNPACK) { enforceBooleanity(unpacked_[i], opcode_); } + } + Algebra::CircuitPolynomial packedPoly(packedPolyLC); + if (packingMode_ == PackingMode::PACK) + + + pb_->addGeneralConstraint(packedPoly + packed_, "packed = packedPoly", opcode_); +} + +void CompressionPacking_Gadget::generateWitness(){ + const int n = unpacked_.size(); + const int bitsPerFieldElem = Algebra::ExtensionDegree; + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + if (packingMode_ == PackingMode::PACK) { + int currentBit = 0; + Algebra::FElem packedVal = Algebra::zero(); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + for (int j = 0; j < bitsPerFieldElem && currentBit < n; ++j, ++currentBit) { + packedVal += x_i * val(unpacked_[currentBit]); + x_i *= x; + } + val(packed_) = packedVal; + return; + } + // else (UNPACK) + GADGETLIB_ASSERT(packingMode_ == PackingMode::UNPACK, + "Packing gadget created with unknown packing mode."); + for (int i = 0; i < n; ++i) { + Algebra::FElem value = pb_->val(packed_); + pb_->val(unpacked_[i]) = Algebra::mapFieldElementToInteger(i, 1, value) ? Algebra::one() : Algebra::zero(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Gadget DoubleUnpack_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +DoubleUnpack_Gadget::DoubleUnpack_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::Variable& packed, + const Opcode opcode) + :Gadget(pb), unpacked1_(unpacked1), unpacked2_(unpacked2), packed_(packed), opcode_(opcode){} + +GadgetPtr DoubleUnpack_Gadget::create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::Variable& packed, + const Opcode opcode){ + GadgetPtr pGadget(new DoubleUnpack_Gadget(pb,unpacked1,unpacked2,packed,opcode)); + pGadget->init(); + return pGadget; +} + +void DoubleUnpack_Gadget::init(){} + +void DoubleUnpack_Gadget::generateConstraints(){ + const int numBits1 = unpacked1_.size(); + const int numBits2 = unpacked2_.size(); + const int bitsPerFieldElem = Algebra::ExtensionDegree; + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + Algebra::LinearCombination packedPoly; + for (int i = 0; i < bitsPerFieldElem && i < numBits1; ++i) { + packedPoly = packedPoly + x_i * unpacked1_[i]; + x_i *= x; + enforceBooleanity(unpacked1_[i], opcode_); + } + for (int i = 0; i+numBits1 < bitsPerFieldElem && i < numBits2; ++i) { + packedPoly = packedPoly + x_i * unpacked2_[i]; + x_i *= x; + enforceBooleanity(unpacked2_[i], opcode_); + } + + Algebra::CircuitPolynomial P(packedPoly+packed_); + + pb_->addGeneralConstraint(P, "packed = packedPoly", opcode_); +} + +void DoubleUnpack_Gadget::generateWitness(){ + const size_t n1 = unpacked1_.size(); + const size_t n2 = unpacked2_.size(); + const size_t bitsPerFieldElem = Algebra::ExtensionDegree; + const size_t v = Algebra::mapFieldElementToInteger(0, bitsPerFieldElem, pb_->val(packed_)); + for (size_t i = 0; i < bitsPerFieldElem && i < n1; ++i) { + pb_->val(unpacked1_[i]) = (v & (size_t(1) << i)) ? Algebra::one() : Algebra::zero(); + } + for (size_t i = 0; i + n1 < bitsPerFieldElem && i < n2; ++i) { + pb_->val(unpacked2_[i]) = (v & ((size_t(1) << n1) << i)) ? Algebra::one() : Algebra::zero(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Gadget Addition_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +Addition_Gadget::Addition_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& input1, + const Algebra::VariableArray& input2, + const Algebra::VariableArray& result, + const Algebra::VariableArray& carry, + const Algebra::Variable& flag, + const Opcode opcode) + :Gadget(pb), input1_(input1), input2_(input2), result_(result), carry_(carry), flag_(flag), opcode_(opcode){} + +GadgetPtr Addition_Gadget::create(ProtoboardPtr pb, + const Algebra::VariableArray& input1, + const Algebra::VariableArray& input2, + const Algebra::VariableArray& result, + const Algebra::VariableArray& carry, + const Algebra::Variable& flag, + const Opcode opcode){ + GadgetPtr pGadget(new Addition_Gadget(pb, input1, input2, result, carry, flag, opcode)); + pGadget->init(); + return pGadget; +} + +void Addition_Gadget::init(){} + +void Addition_Gadget::generateConstraints(){ + size_t wordLength = result_.size(); + const Algebra::Variable a = input1_[0]; + const Algebra::Variable b = input2_[0]; + const Algebra::Variable d = result_[0]; + pb_->addGeneralConstraint(d + a + b, "input1[0] + input2[0] = result[0]", opcode_); + pb_->addGeneralConstraint(a * b + carry_[1], "carry1", opcode_); + for (unsigned int i = 1; i < wordLength; ++i){ + const Algebra::Variable a = input1_[i]; + const Algebra::Variable b = input2_[i]; + const Algebra::Variable c = carry_[i]; + const Algebra::Variable d = result_[i]; + const Algebra::Variable nextCarry = (i < wordLength - 1) ? carry_[i + 1] : flag_; + pb_->addGeneralConstraint(a + b + c + d, "input1[i] + input2[i] = result[i]" , opcode_); + pb_->addGeneralConstraint(nextCarry + a*b + a*c + b*c, "ADD Check next carry/flag", opcode_); + } +} + +void Addition_Gadget::generateWitness(){ + size_t wordLength = result_.size(); + pb_->val(result_[0]) = pb_->val(input1_[0]) + pb_->val(input2_[0]); + pb_->val(carry_[1]) = pb_->val(input1_[0]) * pb_->val(input2_[0]); + for (unsigned int i = 1; i < wordLength; ++i){ + const bool b1 = (val(input1_[i]) == Algebra::one()); + const bool b2 = (val(input2_[i]) == Algebra::one()); + const bool b3 = (val(carry_[i]) == Algebra::one()); + pb_->val(result_[i]) = (b1 ^ b2 ^ b3) ? Algebra::one() : Algebra::zero(); + const Algebra::Variable nextCarry = (i < wordLength - 1) ? carry_[i + 1]: flag_; + pb_->val(nextCarry) = (b1 ? (b2 || b3) : (b2 && b3)) ? Algebra::one() : Algebra::zero(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* MultiplicationPacking Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +MultiplicationPacking_Gadget::MultiplicationPacking_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::VariableArray& partials, + const Algebra::Variable& packed, + const bool isSigned, + const PackingMode packingMode, + const Opcode opcode) + : + Gadget(pb), + packingMode_(packingMode), + unpacked_(unpacked), + partials_(partials), + packed_(packed), + isSigned_(isSigned), + opcode_(opcode){ + GADGETLIB_ASSERT(unpacked_.size() == partials_.size(), "MultPack: array size equality"); +} + +GadgetPtr MultiplicationPacking_Gadget::create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::VariableArray& partials, + const Algebra::Variable& packed, + const bool isSigned, + const PackingMode packingMode, + const Opcode opcode){ + GadgetPtr pGadget(new MultiplicationPacking_Gadget(pb, unpacked, partials, packed, isSigned, packingMode, opcode)); + pGadget->init(); + return pGadget; +} + +void MultiplicationPacking_Gadget::init(){} + +void MultiplicationPacking_Gadget::generateConstraints(){ + Algebra::LinearCombination prev = unpacked_[0] * Algebra::xFE() + Algebra::one() + unpacked_[0]; + unsigned int i; Algebra::FElem g_2_i; Algebra::LinearCombination c; + for (i = 1; i < unpacked_.size()-1 ; i++){ + g_2_i = getGenerator2toTheNthPower(i); + c = ((unpacked_[i] * g_2_i) + (Algebra::one() + unpacked_[i])); + pb_->addGeneralConstraint(partials_[i] + prev * c, "partial[i] = prev*(g ^ (2 ^ i * unpacked[i]))", opcode_); + prev = partials_[i]; + } + g_2_i = isSigned_ ? getGenerator2toTheNthPower(i).inverse() : getGenerator2toTheNthPower(i); + c = ((unpacked_[i] * g_2_i) + (Algebra::one() + unpacked_[i])); + pb_->addGeneralConstraint(packed_ + prev * c, "packed_ = prev*g^(2^(regSize-1) * unpacked[(regSize-1)])", opcode_); + if (packingMode_ == PackingMode::PACK) + return; + GADGETLIB_ASSERT(packingMode_ == PackingMode::UNPACK, + "MultiplicationPacking_Gadget: created with unknown packing mode."); + for (unsigned int i = 0; i < unpacked_.size(); i++) + enforceBooleanity(unpacked_[i], opcode_); +} + +void MultiplicationPacking_Gadget::generateWitness(int n){ + if (PackingMode::UNPACK == packingMode_){ + GADGETLIB_ASSERT(n >= 0, "MultiplicationPacking_Gadget: n should be >= 0."); + unsigned int numberOfDigits = n == 0 ? 0 : floor(log2(n)) + 1; + GADGETLIB_ASSERT(numberOfDigits <= unpacked_.size(), "MultiplicationPacking_Gadget: number of bits in n should be less than the unpakced.size()"); + for (unsigned int i = 0; i < unpacked_.size(); ++i){ + pb_->val(unpacked_[i]) = (n & 1u) ? Algebra::one() : Algebra::zero(); + n >>= 1u; + } + //return; //don't return! + } + else { + GADGETLIB_ASSERT(packingMode_ == PackingMode::PACK, + "MultiplicationPacking_Gadget: created with unknown packing mode."); + } + Algebra::FElem prev = val(unpacked_[0]) * Algebra::xFE() + (Algebra::one() + val(unpacked_[0])); + unsigned int i; Algebra::FElem g_2_i, c; + for (i = 1; i < unpacked_.size()-1 ; i++){ + g_2_i = getGenerator2toTheNthPower(i); + c = ((val(unpacked_[i]) * g_2_i) + (Algebra::one() + val(unpacked_[i]))); + prev = pb_->val(partials_[i]) = prev*c; + } + if (PackingMode::PACK == packingMode_){ + g_2_i = isSigned_ ? getGenerator2toTheNthPower(i).inverse() : getGenerator2toTheNthPower(i); + c = ((val(unpacked_[i]) * g_2_i) + (Algebra::one() + val(unpacked_[i]))); + pb_->val(packed_) = prev*c; + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* DoubleMultiplicationPacking Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +DoubleMultPack_Gadget::DoubleMultPack_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const Algebra::Variable& packed, + const bool isSigned, + const Opcode opcode) + : Gadget(pb), unpacked1_(unpacked1), unpacked2_(unpacked2), + partials1_(partials1), partials2_(partials2), packed_(packed), isSigned_(isSigned), opcode_(opcode){ + GADGETLIB_ASSERT(unpacked1_.size() == unpacked2_.size(), "DMultPack: array size equality"); + GADGETLIB_ASSERT(unpacked1_.size() == partials1_.size(), "DMultPack: array size equality"); + GADGETLIB_ASSERT(unpacked1_.size() == partials2_.size(), "DMultPack: array size equality"); +} + +GadgetPtr DoubleMultPack_Gadget::create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const Algebra::Variable& packed, + const bool isSigned, + const Opcode opcode){ + GadgetPtr pGadget(new DoubleMultPack_Gadget(pb, unpacked1, unpacked2, partials1, partials2, packed, isSigned, opcode)); + pGadget->init(); + return pGadget; +} + +void DoubleMultPack_Gadget::init(){} + +void DoubleMultPack_Gadget::generateConstraints(){ + unsigned int j; Algebra::FElem g_2_i; Algebra::LinearCombination c; + Algebra::LinearCombination prev = unpacked1_[0] * Algebra::xFE() + (Algebra::one() + unpacked1_[0]); + for (unsigned int i = 1; i < unpacked1_.size(); i++){ + g_2_i = getGenerator2toTheNthPower(i); + c = ((unpacked1_[i] * g_2_i) + (Algebra::one() + unpacked1_[i])); + pb_->addGeneralConstraint(partials1_[i] + prev*c, "DMultPack partials1", opcode_); + prev = partials1_[i]; + } + for (j = 0; j < unpacked2_.size()-1 ; j++){ + g_2_i = getGenerator2toTheNthPower(unpacked1_.size()+j); + c = ((unpacked2_[j] * g_2_i) + (Algebra::one() + unpacked2_[j])); + pb_->addGeneralConstraint(partials2_[j] + prev*c, "DMultPack partials2", opcode_); + prev = partials2_[j]; + } + g_2_i = isSigned_ ? + getGenerator2toTheNthPower(unpacked1_.size()+j).inverse() : + getGenerator2toTheNthPower(unpacked1_.size()+j); + c = ((unpacked2_[j] * g_2_i) + (Algebra::one() + unpacked2_[j])); + pb_->addGeneralConstraint(packed_ + prev*c, "DMultPack result", opcode_); +} + +void DoubleMultPack_Gadget::generateWitness(){ + Algebra::FElem prev = val(unpacked1_[0]) * Algebra::FElem(getGF2E_X()) + (Algebra::one() + val(unpacked1_[0])); + for (unsigned int i = 1; i < unpacked1_.size(); i++){ + const Algebra::FElem g_2_i = Algebra::FElem(getGenerator2toTheNthPower(i)); + const Algebra::FElem c = ((val(unpacked1_[i]) * g_2_i) + (Algebra::one() + val(unpacked1_[i]))); + prev = pb_->val(partials1_[i]) = prev*c; + } + for (unsigned int j = 0; j < unpacked2_.size()-1; j++){ + const Algebra::FElem g_2_i = Algebra::FElem(getGenerator2toTheNthPower(unpacked1_.size()+j)); + const Algebra::FElem c = ((val(unpacked2_[j]) * g_2_i) + (Algebra::one() + val(unpacked2_[j]))); + prev = pb_->val(partials2_[j]) = prev*c; + } + //pb_->val(packed_) = prev*c; //don't? TODO: replace with assert +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Multiplication Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +Multiplication_Gadget::Multiplication_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const bool isSigned, + const Opcode opcode) + : + Gadget(pb), + unpacked1_(unpacked1), + unpacked2_(unpacked2), + partials1_(partials1), + partials2_(partials2), + opcode_(opcode), + isSigned_(isSigned), + idxLast(unpacked1.size() - 1){ + GADGETLIB_ASSERT(unpacked1_.size() == unpacked2_.size(), "Mult: array size equality"); + GADGETLIB_ASSERT(unpacked1_.size() == partials1_.size(), "Mult: array size equality"); + GADGETLIB_ASSERT(unpacked1_.size() == partials2_.size(), "Mult: array size equality"); +} + +GadgetPtr Multiplication_Gadget::create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const bool isSigned, + const Opcode opcode){ + GadgetPtr pGadget(new Multiplication_Gadget(pb, unpacked1, unpacked2, partials1, partials2, isSigned, opcode)); + pGadget->init(); + return pGadget; +} + +void Multiplication_Gadget::init(){ + multPack1_g_ = MultiplicationPacking_Gadget::create(pb_, unpacked1_, partials1_, partials1_[idxLast], isSigned_, PackingMode::PACK, opcode_); +} + +void Multiplication_Gadget::generateConstraints(){ + multPack1_g_->generateConstraints(); + const Algebra::Variable h = partials1_[idxLast]; + Algebra::CircuitPolynomial c; //not LinearCombination + if (isSigned_){ + const Algebra::Variable invh = partials1_[0]; + pb_->addGeneralConstraint(h * invh + Algebra::one(), "sMult inv", opcode_); + c = unpacked2_[idxLast] * invh + Algebra::one() + unpacked2_[idxLast]; + } + else { + c = unpacked2_[idxLast] * h + Algebra::one() + unpacked2_[idxLast]; + } + pb_->addGeneralConstraint(partials2_[idxLast] + c, "Mult partials2 init", opcode_); + for (int i = idxLast - 1; i >= 0; --i){ + c = unpacked2_[i] * h + Algebra::one() + unpacked2_[i]; + pb_->addGeneralConstraint(partials2_[i] + partials2_[i + 1] * partials2_[i + 1] * c, "Mult partials2", opcode_); + } +} + +void Multiplication_Gadget::generateWitness(){ + std::dynamic_pointer_cast(multPack1_g_)->generateWitness(); + const Algebra::FElem h = val(partials1_[idxLast]); + Algebra::FElem c; + const Algebra::Variable invh = partials1_[0]; + if (isSigned_) { + c = h.inverse(); + pb_->val(invh) = c; + } + else { + c = h; + pb_->val(invh) = Algebra::zero(); + } + pb_->val(partials2_[idxLast]) = Algebra::one() != val(unpacked2_[idxLast]) ? Algebra::one() : c; + for (int i = idxLast - 1; i >= 0; --i){ + c = Algebra::one() == val(unpacked2_[i]) ? h : Algebra::one(); + pb_->val(partials2_[i]) = val(partials2_[i + 1])*val(partials2_[i + 1])*c; + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Greater Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +GreaterEqual_Gadget::GreaterEqual_Gadget(ProtoboardPtr pb, + const Algebra::UnpackedWord& var1, + const Algebra::UnpackedWord& var2, + const Algebra::UnpackedWord& flags, + const Algebra::FlagVariable& flagGEQ, + const bool isSigned, + const Opcode opcode) + : Gadget(pb), var1_(var1), var2_(var2), flags_(flags), flagGEQ_(flagGEQ), isSigned_(isSigned), opcode_(opcode) { + GADGETLIB_ASSERT(var2_.size() == var1_.size(), "Greater Gadget: The length of the unpacked representations should be equal"); + GADGETLIB_ASSERT(flags_.size() == var1_.size(), "Greater Gadget: size of the flags array should be equal to unpacked representation size"); +} + +void GreaterEqual_Gadget::init(){} + +GadgetPtr GreaterEqual_Gadget::create(ProtoboardPtr pb, + const Algebra::UnpackedWord& var1, + const Algebra::UnpackedWord& var2, + const Algebra::UnpackedWord& flags, + const Algebra::FlagVariable& flagGEQ, + const bool isSigned, + const Opcode opcode){ + GadgetPtr pGadget(new GreaterEqual_Gadget(pb, var1, var2, flags, flagGEQ, isSigned, opcode)); + pGadget->init(); + return pGadget; +} + +void GreaterEqual_Gadget::generateConstraints(){ + /* Break Down of the constraints: + flags_[unpacked.size()] = g; + + for all i from unpacked.size() - 1 to 0: + (1) flags_[i] + + (2) + flags_[i+1] * (1 + flags_[i+1]) *(g* (1 + g))^-1 *[ g*(a_i + b_i + 1) + a_i*(b_i + 1)] + (3) + flags_[i+1] * (g + flags_[i+1]) *(1+g) ^ -1 + + if flag[i+1] = 0 then flag[i] = 0. Lines(1) and (2) are immediately 0 + if flag[i+1] = 1 then flag[i] = 1. Line (1) is 0 Line (2) equals (1*(g+1)*(1+g)^-1) == 1 + if flag[i+1] = g and a_i = b_i then flag[i] = g. Line (2) is 0 and line (1) equals (1*(1+g) * (1+g)^-1) * g = g + if flag[i+1] = g and a_i = 1 and b_1=0 then flag[i] = 1. Line (2) is 0 and line (1) equals (1*(1+g) * (1+g)^-1) *[1] = 1 + if flag[i+1] = g and a_i = 0 and b_1=1 then flag[i] = 0. Line (2) is 0 and line (1) equals (1*(1+g) * (1+g)^-1) *[0] = 1 + */ + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + const Algebra::FElem invOnePlusG = (g + Algebra::one()).inverse(); // (1+g)^-1 + const Algebra::FElem invGPlusGG = (g*(g + Algebra::one())).inverse(); // (g*(1+g))^-1 + int first = flags_.size() - 1 - int(isSigned_); + Algebra::Variable currFlag; + for (int i = first; i >= 0; i--){ + Algebra::CircuitPolynomial tmpPoly = g * (var1_[i] + var2_[i] + Algebra::one()) + (var1_[i] * (var2_[i] + Algebra::one())); + if (first == i) { + pb_->addGeneralConstraint(flags_[i] + tmpPoly, std::to_string(i) + ":flags", opcode_); //": flag[i] + g*(var1_[i] + var2_[i] + 1) + var1_[i] * (var2_[i] + 1)", opcode_); + continue; + } + tmpPoly = tmpPoly * ((flags_[i + 1] * (Algebra::one() + flags_[i + 1])) * invGPlusGG); + tmpPoly = tmpPoly + flags_[i + 1] * (flags_[i + 1] + g) * invOnePlusG; + if (i > 0) + currFlag = flags_[i]; + else { + currFlag = flagGEQ_; + if (isSigned_) { + ++first; + tmpPoly = tmpPoly*(Algebra::one()+var1_[first]+var2_[first]) + (Algebra::one()+var1_[first])*var2_[first]; + } + } + pb_->addGeneralConstraint(currFlag + tmpPoly, std::to_string(i) + ":flags", opcode_); //": flag[i] + flag[i+1]*(flag[i+1] + 1)*(g*(g+1))^-1[g*(var1_[i]+var2_[i] +1) +var1_[i]*(var2_[i] + 1)] + flag[i+1]*(flag[i+1] + g) * (1 + g)^-1", opcode_); + } +} + +void GreaterEqual_Gadget::generateWitness(){ + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + int i = flags_.size() - 1 - int(isSigned_); + pb_->val(flags_[i]) = val(var1_[i])==val(var2_[i]) ? g : val(var1_[i]); + Algebra::Variable currFlag; + Algebra::FElem a, b; + while (--i >= 0){ + const Algebra::FElem prevFlag = pb_->val(flags_[i + 1]); + if (i > 0) + currFlag = flags_[i]; + else { + if (isSigned_) { + a = pb_->val(var1_[flags_.size() - 1]); + b = pb_->val(var2_[flags_.size() - 1]); + if (a != b) { + pb_->val(flagGEQ_) = b; //positive b -> flag=0, negative b -> flag=1 + return; + } + } + currFlag = flagGEQ_; + } + if (prevFlag == Algebra::one() || prevFlag == Algebra::zero()){ + pb_->val(currFlag) = prevFlag; + } + else{ //prevFlag == g; + a = pb_->val(var1_[i]); + b = pb_->val(var2_[i]); + if (a == b){ + pb_->val(currFlag) = prevFlag; + } + else{ // a != b + pb_->val(currFlag) = a; // if a = 1 and b = 0 -> flags = 1. + // if a = 0 and b = 1 -> flags = 0. + } + } + } +} + +} // namespace gadgetlib diff --git a/tinyram/gadgetlib/gadgetlib/gadget.hpp b/tinyram/gadgetlib/gadgetlib/gadget.hpp new file mode 100644 index 0000000..65285b0 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/gadget.hpp @@ -0,0 +1,369 @@ +/** @file + ***************************************************************************** + Interfaces and basic gadgets for R1P (Rank 1 prime characteristic) + constraint systems. + + These interfaces have been designed to allow later adding other fields or constraint + structures while allowing high level design to stay put. + + A gadget represents (and generates) the constraints, constraint "wiring", and + witness for a logical task. This is best explained using the physical design of a printed + circuit. The Protoboard is the board on which we will "solder" our circuit. The wires + (implemented by Variables) can hold any element of the underlying field. Each constraint + enforces a relation between wires. These can be thought of as gates. + + The delegation of tasks is as follows: + + - Constructor - Allocates all Variables to a Protoboard. Creates all sub-gadgets + that will be needed and wires their inputs and outputs. + generateConstraints - Generates the constraints which define the + necessary relations between the previously allocated Variables. + + - generateWitness - Generates an assignment for all non-input Variables which is + consistent with the assignment of the input Variables and satisfies + all of the constraints. In essence, this computes the logical + function of the Gadget. + + - create - A static factory method used for construction of the Gadget. This is + used in order to create a Gadget without explicit knowledge of the + underlying algebraic field. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef GADGETLIB3_GADGETLIB3_GADGET_HPP_ +#define GADGETLIB3_GADGETLIB3_GADGET_HPP_ + +#include +#include +#include +#include +#include +#include "gadgetMacros.hpp" + +namespace gadgetlib { + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +/** + Gadget class, representing the constraints and witness generation for a logical task. + + Gadget hierarchy: + LD2 = Low Degree constraints over characteristic 2 extension field. + + Gadgets have a somewhat cumbursome class heirarchy, for the sake of clean gadget construction. + (1) A field agnostic, concrete (as opposed to interface) gadget will derive from Gadget. For + instance NAND needs only AND and NOT and does not care about the field, thus it derives from + Gadget. + (2) Field specific interface class R1P_Gadget derives from Gadget using virtual + inheritance, in order to avoid the Dreaded Diamond problem (see + http://stackoverflow.com/a/21607/1756254 for more info) + (3) Functional interface classes such as LooseMUX_GadgetBase virtually derive from Gadget and + define special gadget functionality. For gadgets with no special interfaces we use the macro + CREATE_GADGET_BASE_CLASS() for the sake of code consistency (these gadgets can work the same + without this base class). This is an interface only and the implementation of AND_Gadget is + field specific. + (4) These field specific gadgets will have a factory class with static method create, such as + AND_Gadget::create(...) in order to agnostically create this gadget for use by a field + agnostic gadget. + (5) Concrete field dependant gadgets derive via multiple inheritance from two interfaces. + e.g. R1P_AND_Gadget derives from both AND_Gadget and R1P_Gadget. This was done to allow usage + of AND_Gadget's field agnostic create() method and R1P_Gadget's field specific val() method. +*/ +class Gadget { +private: + DISALLOW_COPY_AND_ASSIGN(Gadget); +protected: + ProtoboardPtr pb_; +public: + Gadget(ProtoboardPtr pb); + virtual void init() = 0; + /* generate constraints must have this interface, however generateWitness for some gadgets + (like CTime) will take auxiliary information (like memory contents). We do not want to force + the interface for generateWitness but do want to make sure it is never invoked from base + class. + */ + virtual void generateConstraints() = 0; + virtual void generateWitness(); // Not abstract as this method may have different signatures. + void addGeneralConstraint(const Algebra::CircuitPolynomial& polynomial, + const ::std::string& name, Opcode opcode = Opcode::NONE); + void enforceBooleanity(const Algebra::Variable& var,Opcode opcode = Opcode::NONE) {pb_->enforceBooleanity(var,opcode);} + Algebra::FElem& val(const Algebra::Variable& var) {return pb_->val(var);} + const Algebra::FElem val(const Algebra::Variable& var) const { return pb_->val(var); } + const Algebra::FElem val(const Algebra::LinearCombination& lc) const {return pb_->val(lc);} + bool flagIsSet(const Algebra::FlagVariable& flag) const {return pb_->flagIsSet(flag);} +}; + +typedef ::std::shared_ptr GadgetPtr; // Not a unique_ptr because sometimes we need to cast + // these pointers for specific gadget operations. +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Packing Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +enum class PackingMode : bool { PACK, UNPACK }; + +class CompressionPacking_Gadget : public Gadget{ +private: + PackingMode packingMode_; + Algebra::VariableArray unpacked_; + Algebra::Variable packed_; + Opcode opcode_; + CompressionPacking_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::Variable& packed, + PackingMode packingMode, + Opcode opcode); + virtual void init(); // private in order to force programmer to invoke from a Gadget* only +public: + + void generateConstraints(); + void generateWitness(); + static GadgetPtr create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::Variable& packed, + PackingMode packingMode, + Opcode opcode); +private: + DISALLOW_COPY_AND_ASSIGN(CompressionPacking_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Double-Unpack Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class DoubleUnpack_Gadget : public Gadget{ +private: + Algebra::VariableArray unpacked1_; + Algebra::VariableArray unpacked2_; + Algebra::Variable packed_; + Opcode opcode_; + DoubleUnpack_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::Variable& packed, + const Opcode opcode); + virtual void init(); // private in order to force programmer to invoke from a Gadget* only +public: + void generateConstraints(); + void generateWitness(); + static GadgetPtr create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::Variable& packed, + const Opcode opcode); +private: + DISALLOW_COPY_AND_ASSIGN(DoubleUnpack_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Addition Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class Addition_Gadget : public Gadget{ +private: + Algebra::VariableArray input1_; + Algebra::VariableArray input2_; + Algebra::VariableArray result_; + Algebra::VariableArray carry_; + Algebra::Variable flag_; + Opcode opcode_; + Addition_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& input1, + const Algebra::VariableArray& input2, + const Algebra::VariableArray& result, + const Algebra::VariableArray& carry, + const Algebra::Variable& flag, + const Opcode opcode); + virtual void init(); // private in order to force programmer to invoke from a Gadget* only +public: + void generateConstraints(); + void generateWitness(); + static GadgetPtr create(ProtoboardPtr pb, + const Algebra::VariableArray& input1, + const Algebra::VariableArray& input2, + const Algebra::VariableArray& result, + const Algebra::VariableArray& carry, + const Algebra::Variable& flag, + const Opcode opcode); +private: + DISALLOW_COPY_AND_ASSIGN(Addition_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* MultiplicationPacking Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class MultiplicationPacking_Gadget : public Gadget{ +private: + MultiplicationPacking_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::VariableArray& partials, + const Algebra::Variable& packed, + const bool isSigned, + const PackingMode packingMode, + const Opcode opcode); + virtual void init(); + PackingMode packingMode_; + Algebra::VariableArray unpacked_; + Algebra::VariableArray partials_; + Algebra::Variable packed_; + bool isSigned_; + Opcode opcode_; +public: + void generateConstraints(); + void generateWitness(int n = -1); + static GadgetPtr create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked, + const Algebra::VariableArray& partials, + const Algebra::Variable& packed, + const bool isSigned, + const PackingMode packingMode, + Opcode opcode); +private: + DISALLOW_COPY_AND_ASSIGN(MultiplicationPacking_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* DoubleMultiplicationPacking Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class DoubleMultPack_Gadget : public Gadget{ +private: + DoubleMultPack_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const Algebra::Variable& packed, + const bool isSigned, + const Opcode opcode); + virtual void init(); + Algebra::VariableArray unpacked1_; + Algebra::VariableArray unpacked2_; + Algebra::VariableArray partials1_; + Algebra::VariableArray partials2_; + Algebra::Variable packed_; + bool isSigned_; + Opcode opcode_; +public: + void generateConstraints(); + void generateWitness(); + static GadgetPtr create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const Algebra::Variable& packed, + const bool isSigned, + const Opcode opcode); +private: + DISALLOW_COPY_AND_ASSIGN(DoubleMultPack_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Multiplication Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class Multiplication_Gadget : public Gadget{ +private: + Multiplication_Gadget(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const bool isSigned, + const Opcode opcode); + virtual void init(); + Algebra::VariableArray unpacked1_; + Algebra::VariableArray unpacked2_; + Algebra::VariableArray partials1_; + Algebra::VariableArray partials2_; + Opcode opcode_; + bool isSigned_; + int idxLast; + GadgetPtr multPack1_g_; +public: + void generateConstraints(); + void generateWitness(); + static GadgetPtr create(ProtoboardPtr pb, + const Algebra::VariableArray& unpacked1, + const Algebra::VariableArray& unpacked2, + const Algebra::VariableArray& partials1, + const Algebra::VariableArray& partials2, + const bool isSigned, + const Opcode opcode); +private: + DISALLOW_COPY_AND_ASSIGN(Multiplication_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* Greater Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class GreaterEqual_Gadget : public Gadget{ +private: + GreaterEqual_Gadget(ProtoboardPtr pb, + const Algebra::UnpackedWord& var1, + const Algebra::UnpackedWord& var2, + const Algebra::UnpackedWord& flags, + const Algebra::FlagVariable& flagGEQ, + const bool isSigned, + const Opcode opcode); + virtual void init(); // private in order to force programmer to invoke from a Gadget* only + + Algebra::UnpackedWord var1_; + Algebra::UnpackedWord var2_; + Algebra::UnpackedWord flags_; + Algebra::FlagVariable flagGEQ_; + bool isSigned_; + Opcode opcode_; + unsigned int unpakcedSize_; +public: + static GadgetPtr create(ProtoboardPtr pb, + const Algebra::UnpackedWord& var1, + const Algebra::UnpackedWord& var2, + const Algebra::UnpackedWord& flags, + const Algebra::FlagVariable& flagGEQ, + const bool isSigned, + const Opcode opcode); + void generateConstraints(); + void generateWitness(); +}; + +} // namespace gadgetlib + +#endif // GADGETLIB3_GADGETLIB3_GADGET_HPP_ \ No newline at end of file diff --git a/tinyram/gadgetlib/gadgetlib/gadgetMacros.hpp b/tinyram/gadgetlib/gadgetlib/gadgetMacros.hpp new file mode 100644 index 0000000..1051076 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/gadgetMacros.hpp @@ -0,0 +1,308 @@ +/** @file + ***************************************************************************** + Macros for quick construction of interface and factory classes for non field + agnostic gadgets. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef LIBSNARK_GADGETLIB3_INCLUDE_GADGETLIB3_GADGETMACROS_HPP_ +#define LIBSNARK_GADGETLIB3_INCLUDE_GADGETLIB3_GADGETMACROS_HPP_ + +// The macro below counts the number of arguments sent with __VA_ARGS__ +// it has not been tested yet. Due to a current MSVC bug it is not in use yet. + +/** + Macro which creates Base classes for function specific gadgets. For instance + CREATE_GADGET_BASE_CLASS(AND_GadgetBase) will create a base class which should be inherited by + R1P_AND_Gadget and ANOTHER_FIELD_AND_Gadget. The Factory class which makes a field agnostic + gadget will be created by the CREATE_GADGET_FACTORY_CLASS(AND_Gadget, ...) macro +*/ +#define CREATE_GADGET_BASE_CLASS(GadgetBase) \ +class GadgetBase : virtual public Gadget { \ +protected: \ + GadgetBase(ProtoboardPtr pb) : Gadget(pb) {} \ +public: \ + virtual ~GadgetBase() = 0; \ +private: \ + virtual void init() = 0; \ + DISALLOW_COPY_AND_ASSIGN(GadgetBase); \ +}; // class GadgetBase + + + +/** + Macro for creating gadget factory classes. For instance + CREATE_GADGET_FACTORY_CLASS(AND_Gadget, 2, VariableArray, input, Variable, result); creates a + class AND_Gadget with the method: + GadgetPtr AND_Gadget::create(ProtoboardPtr pb, + const VariableArray& input, + const Variable& result) + which will instantiate a field specific gadget depending on the Protoboard type. + This macro implements the factory design pattern. +*/ +#define ADD_ELLIPSES_1(Type1, name1) \ + const Type1 & name1 + +#define ADD_ELLIPSES_2(Type1, name1, Type2, name2) \ + const Type1 & name1, const Type2 & name2 + +#define ADD_ELLIPSES_3(Type1, name1, Type2, name2, Type3, name3) \ + const Type1 & name1, const Type2 & name2, const Type3 & name3 + +#define ADD_ELLIPSES_4(Type1, name1, Type2, name2, Type3, name3, Type4, name4) \ + const Type1 & name1, const Type2 & name2, const Type3 & name3, const Type4 & name4 + +#define ADD_ELLIPSES_5(Type1, name1, Type2, name2, Type3, name3, Type4, name4, Type5, name5) \ + const Type1 & name1, const Type2 & name2, const Type3 & name3, const Type4 & name4, \ + const Type5 & name5 + +#define ADD_ELLIPSES_7(Type1, name1, Type2, name2, Type3, name3, Type4, name4, Type5, name5, \ + Type6, name6, Type7, name7, Type8, name8, Type9, name9) \ + const Type1 & name1, const Type2 & name2, const Type3 & name3, const Type4 & name4, \ + const Type5 & name5, const Type6 & name6, const Type7 & name7 + +#define ADD_ELLIPSES_8(Type1, name1, Type2, name2, Type3, name3, Type4, name4, Type5, name5, \ + Type6, name6, Type7, name7, Type8, name8) \ + const Type1 & name1, const Type2 & name2, const Type3 & name3, const Type4 & name4, \ + const Type5 & name5, const Type6 & name6, const Type7 & name7, const Type8 & name8 + +#define ADD_ELLIPSES_9(Type1, name1, Type2, name2, Type3, name3, Type4, name4, Type5, name5, \ + Type6, name6, Type7, name7, Type8, name8, Type9, name9) \ + const Type1 & name1, const Type2 & name2, const Type3 & name3, const Type4 & name4, \ + const Type5 & name5, const Type6 & name6, const Type7 & name7, const Type8 & name8, \ + const Type9 & name9 + +/* + This was supposed to be a variadic macro CREATE_GADGET_FACTORY_CLASS(...) which invokes the + correct number of arguments. Due to an MSVC bug and lack of time it will currently be copied + with different names. + Hopefully some day I will have time to find a workaround / use Boost preprocessor instead. + The MSVC bug (feature...) is that __VA_ARGS__ passes to sub macros as 1 argument, so defining + the following: + #define CREATE_GADGET_FACTORY_CLASS(__VA_ARGS__) \ + CREATE_GADGET_FACTORY_CLASS_ ## PP_NARG(__VA_ARGS__)(__VA_ARGS__) + will always create CREATE_GADGET_FACTORY_CLASS_1(__VA_ARGS__) + Moreover, this macro considers __VA_ARGS__ to be only 1 argument! +*/ +#define CREATE_GADGET_FACTORY_CLASS_1(GadgetType, Type1, name1) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, ADD_ELLIPSES_1(Type1, name1)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + +#define CREATE_GADGET_FACTORY_CLASS_2(GadgetType, Type1, name1, Type2, name2) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, \ + ADD_ELLIPSES_2(Type1, name1, Type2, name2)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1, name2)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1, name2)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + +#define CREATE_GADGET_FACTORY_CLASS_3(GadgetType, Type1, name1, Type2, name2, Type3, name3) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, \ + ADD_ELLIPSES_3(Type1, name1, Type2, name2, Type3, name3)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1, name2, name3)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1, name2, name3)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + +#define CREATE_GADGET_FACTORY_CLASS_4(GadgetType, Type1, name1, Type2, name2, Type3, name3, \ + Type4, name4) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, \ + ADD_ELLIPSES_4(Type1, name1, Type2, name2, Type3, name3, Type4, name4)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1, name2, name3, name4)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1, name2, name3, name4)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + +#define CREATE_GADGET_FACTORY_CLASS_5(GadgetType, Type1, name1, Type2, name2, Type3, name3, \ + Type4, name4, Type5, name5) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, \ + ADD_ELLIPSES_5(Type1, name1, Type2, name2, Type3, name3, Type4, name4, \ + Type5, name5)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1, name2, name3, name4, name5)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1, name2, name3, name4, name5)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + +#define CREATE_GADGET_FACTORY_CLASS_7(GadgetType, Type1, name1, Type2, name2, Type3, name3, \ + Type4, name4, Type5, name5, Type6, name6, Type7, name7) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, \ + ADD_ELLIPSES_7(Type1, name1, Type2, name2, Type3, name3, Type4, name4, \ + Type5, name5, Type6, name6, Type7, name7)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1, name2, name3, name4, name5, name6, \ + name7)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1, name2, name3, name4, name5, name6, \ + name7)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + + +#define CREATE_GADGET_FACTORY_CLASS_8(GadgetType, Type1, name1, Type2, name2, Type3, name3, \ + Type4, name4, Type5, name5, Type6, name6, Type7, name7, \ + Type8, name8) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, \ + ADD_ELLIPSES_8(Type1, name1, Type2, name2, Type3, name3, Type4, name4, \ + Type5, name5, Type6, name6, Type7, name7, Type8, name8)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1, name2, name3, name4, name5, name6, \ + name7, name8)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1, name2, name3, name4, name5, name6, \ + name7, name8)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + +#define CREATE_GADGET_FACTORY_CLASS_9(GadgetType, Type1, name1, Type2, name2, Type3, name3, \ + Type4, name4, Type5, name5, Type6, name6, Type7, name7, \ + Type8, name8, Type9, name9) \ +class GadgetType { \ +public: \ + static GadgetPtr create(ProtoboardPtr pb, \ + ADD_ELLIPSES_9(Type1, name1, Type2, name2, Type3, name3, Type4, name4, \ + Type5, name5, Type6, name6, Type7, name7, Type8, name8, \ + Type9, name9)) { \ + addGadgetToTraceback(#GadgetType); \ + GadgetPtr pGadget; \ + if (pb->fieldType() == FieldType::R1P) { \ + pGadget.reset(new R1P_ ## GadgetType(pb, name1, name2, name3, name4, name5, name6, \ + name7, name8, name9)); \ +/* #ifndef REMOVE_LD2_CODE \\ Do not remove this comment, it is for an external deploy script */ \ + } else if (pb->fieldType() == FieldType::LD2) { \ + pGadget.reset(new LD2_ ## GadgetType(pb, name1, name2, name3, name4, name5, name6, \ + name7, name8, name9)); \ +/* #endif \\ REMOVE_LD2_CODE, Do not remove this comment, it is for an external deploy script */ \ + } else { \ + GADGETLIB_FATAL("Attempted to create gadget of undefined Protoboard type."); \ + } \ + pGadget->init(); \ + removeGadgetFromTraceback(#GadgetType); \ + return pGadget; \ + } \ +private: \ + DISALLOW_CONSTRUCTION(GadgetType); \ + DISALLOW_COPY_AND_ASSIGN(GadgetType); \ +}; // class GadgetType + +#endif // LIBSNARK_GADGETLIB3_INCLUDE_GADGETLIB3_GADGETMACROS_HPP_ diff --git a/tinyram/gadgetlib/gadgetlib/infrastructure.cpp b/tinyram/gadgetlib/gadgetlib/infrastructure.cpp new file mode 100644 index 0000000..7517972 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/infrastructure.cpp @@ -0,0 +1,126 @@ +/** @file +***************************************************************************** +Common functionality needed by many components. +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#include + +#include +#include +#include +#include +#ifdef __linux__ +#include +#include +#endif +#ifdef __GLIBC__ +#include // backtraces +#endif + +namespace gadgetlib { + + /********************************************************/ + /*************** Debug String Formatting ****************/ + /********************************************************/ + +#ifdef DEBUG + const static size_t MAX_FMT = 256; + ::std::string GADGETLIB3_FMT(const char* format, ...) { + char buf[MAX_FMT]; + va_list args; + va_start(args, format); +#ifdef __linux__ + const int strChk = snprintf(buf, MAX_FMT, format, args); +#else // #ifdef __linux__ + const int strChk = vsnprintf_s(buf, MAX_FMT, MAX_FMT, format, args); +#endif // #ifdef __linux__ + va_end(args); + GADGETLIB_ASSERT(strChk >= 0 && strChk < (int)MAX_FMT, "String length larger than buffer. Shorten" + " string or increase buffer size defined in \"MAX_FMT\"."); + return ::std::string(buf); + } +#else // not DEBUG + ::std::string GADGETLIB3_FMT(const char* format, ...) { return ""; } +#endif + + /** Safely converts 64-bit types to 32-bit. */ + long safeConvert(const int64_t num) { + assert(num <= INT_MAX && num >= INT_MIN); + return (long)num; + } + + /*****************************************************************************/ + /*********************** ErrorHandling********** ****************************/ + /*****************************************************************************/ + + /* + TODO add dumping of environment variables and run command to a log file and add log file path + to release mode error message. We don't want people running release version to get any internal + information (variable values, stack trace, etc.) but want to have every data possible to + reproduce assertion. + */ + void ErrorHandling::fatalError(const ::std::string& msg) { +# ifdef DEBUG + ::std::cerr << "ERROR: " << msg << ::std::endl << ::std::endl; + printStacktrace(); + throw ::std::runtime_error(msg); +# else // not DEBUG + const ::std::string releaseMsg("Fatal error encoutered. Run debug build for more" + " information and stack trace."); + ::std::cerr << "ERROR: " << releaseMsg << ::std::endl << ::std::endl; + throw ::std::runtime_error(releaseMsg); +# endif + } + + void ErrorHandling::fatalError(const ::std::stringstream& msg) { + fatalError(msg.str()); + } + + void ErrorHandling::printStacktrace() { +#ifdef __GNUC__ + std::cerr << "Stack trace (pipe through c++filt to demangle identifiers):" << std::endl; + const int maxFrames = 100; + void* frames[maxFrames]; + // Fill array with pointers to stack frames + int numFrames = backtrace(frames, maxFrames); + // Decode frames and print them to stderr + backtrace_symbols_fd(frames, numFrames, STDERR_FILENO); +#else + //TODO make this available for Windows + std::cerr << " (stack trace not available on this platform)" << std::endl; +#endif // __GNUC__ + } + + /*****************************************************************************/ + /**************************** Basic Math ***********************************/ + /*****************************************************************************/ + + double Log2(double n) { + return log(n) / log((double)2); + } + + //returns the ceiling of log2(i) - note that the Log2ceil method currently returns this value + 1 when i is a power of 2. + unsigned int Log2ceiled(uint64_t n) { + return std::ceil(log(n) / log((double)2)); + } + + + /// Returns an upper bound on log2(i). Namely, returns the number of binary digits needed to store + /// the value 'i'. When i == 0 returns 0. + unsigned int Log2ceil(uint64_t i) { + int retval = i ? 1 : 0; + while (i >>= 1) { ++retval; } + return retval; + } + + ///Returns true iff x is a power of 2 + bool IsPower2(const long x) { + return ((x > 0) && ((x & (x - 1)) == 0)); + } + +} // namespace gadgetlib + diff --git a/tinyram/gadgetlib/gadgetlib/infrastructure.hpp b/tinyram/gadgetlib/gadgetlib/infrastructure.hpp new file mode 100644 index 0000000..56c8480 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/infrastructure.hpp @@ -0,0 +1,119 @@ +#ifndef GADGETLIB3_INFRASTRUCTURE_HPP_ +#define GADGETLIB3_INFRASTRUCTURE_HPP_ + + +#include +#include +#include +#include +#include + +#ifndef _MSC_VER // emulate the MSVC-specific sprintf_s using the standard snprintf +#define sprintf_s snprintf //TODO: sprintf_s!=snprintf (http://blog.verg.es/2008/09/sprintfs-is-not-snprintf.html) +#endif + +#ifdef _DEBUG // MSVC Debug build +#define DEBUG // gcc Debug flag +#endif + +/********************************************************/ +/**************** Class Writing Helpers *****************/ +/********************************************************/ +// A macro to disallow any non-defined constructors +// This should be used in the private: declarations for a class +#define DISALLOW_CONSTRUCTION(TypeName) \ + TypeName(); + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +/********************************************************/ +/*************** Debug String Formatting ****************/ +/********************************************************/ + +namespace gadgetlib { + // someday, if/when MSVC supports C++0x variadic templates, change FMT in release version to the + // following in order to increase efficiency: + // #define GADGETLIB2_FMT(...) "" + ::std::string GADGETLIB2_FMT(const char* format, ...); + + /** Safely converts 64-bit types to 32-bit, or from unsigned to signed */ + long safeConvert(const int64_t num); + + /********************************************************/ + /******************* Error Handling *********************/ + /********************************************************/ + + // declare a function as never returning, to quiet down "control reaches end of non-void function" warnings +#if defined(_MSC_VER) // VisualC++ +#define __noreturn _declspec(noreturn) +#elif defined(__GNUC__) +#define __noreturn __attribute__((noreturn)) +#else +#define __noreturn +#endif + + + + /** + * The ErrorHandling class containimplements the functionality of displaying the content of error + * messages (including content of call stack when error happened), and exiting the program. + */ + class ErrorHandling { + public: + static void __noreturn fatalError(const ::std::string& msg); + static void __noreturn fatalError(const std::stringstream& msg); + static void printStacktrace(); + + }; + +#define GADGETLIB_FATAL(msg) do { \ + ::std::stringstream msgStream; \ + msgStream << msg << " (In file " << __FILE__ << " line " << __LINE__ << ".)"; \ + gadgetlib::ErrorHandling::fatalError(msgStream.str()); \ + } while (0) + + // TODO change GADGETLIB_ASSERT to not run in debug +#define GADGETLIB_ASSERT(predicate, msg) if(!(bool(predicate))) GADGETLIB_FATAL(msg); + + /********************************************************/ + /****************** Basic Math **************************/ + /********************************************************/ + + double Log2(double n); + + //returns the ceiling of log2(i) - note that the Log2ceil method currently returns this value + 1 when i is a power of 2. + unsigned int Log2ceiled(uint64_t i); + + + //Calculates upper bound of Log2 of a number (number of bits needed to represent value) + unsigned int Log2ceil(uint64_t i); + + //Returns true iff the given number is a power of 2. + bool IsPower2(const long x); + + + //Returns a^b when a can be a and b are INTEGERS. + //constexpr int64_t POW(int64_t base, int exponent) { + // return (int64_t) powl((long double)base, (long double)exponent); + //} + //#define POW(a,b) ((int64_t)(pow((float)(a),(int)(b)))) + + // Returns 2^exponent + /*constexpr*/ inline int64_t POW2(int exponent) { + //assert(exponent>=0); + return ((int64_t)1) << exponent; + } + + //Returns the ceiling of a when a is of type double. + /*constexpr*/ inline int64_t CEIL(double a) { + return (int64_t)ceil(a); + } + //#define CEIL(a) ((int64_t)ceil((double)(a))) + +} // namespace gadgetlib + +#endif diff --git a/tinyram/gadgetlib/gadgetlib/protoboard.cpp b/tinyram/gadgetlib/gadgetlib/protoboard.cpp new file mode 100644 index 0000000..493477c --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/protoboard.cpp @@ -0,0 +1,178 @@ +/** @file +***************************************************************************** +Implementation of Protoboard, a "memory manager" for building arithmetic constraints +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#include +#include +#include + + +namespace gadgetlib { + +unsigned int Protoboard::numConstraints() const{ + return numConstratins_; +} + +Algebra::Variable::set Protoboard::getUsedVariables() const{ + Algebra::Variable::set constraintsVariables = constraintSystem_.getUsedVariables(); + for (ConstraintSystem opcodeConstraintSystem : opcodeConstraintSystem_){ + Algebra::Variable::set opcodeConstraintVariable = opcodeConstraintSystem.getUsedVariables(); + constraintsVariables.insert(opcodeConstraintVariable.begin(), opcodeConstraintVariable.end()); + } + return constraintsVariables; +} + +Algebra::FElem& Protoboard::val(const Algebra::Variable& var){ + return assignment_[var]; +} + +const Algebra::FElem Protoboard::val(const Algebra::Variable& var) const{ + const Algebra::FElem& retval = assignment_.at(var); + return retval; +} + +const Algebra::FElem Protoboard::val(const Algebra::LinearCombination& lc) const{ + return lc.eval(assignment_); +} + +void Protoboard::addGeneralConstraint(const Polynomial& a, + const ::std::string& name, + Opcode opcode){ + if (opcode == Opcode::MEMORY){ + addMemoryConstraint(a, name); + return; + } + if (opcode == Opcode::NONE){ + constraintSystem_.addConstraint(a, name); + return; + } + else{ + int value = int(opcode); + ConstraintSystem& cs = opcodeConstraintSystem_[value]; + cs.addConstraint(a, name); + } +} + +void Protoboard::addMemoryConstraint(const Polynomial& a, + const ::std::string& name){ + memoryConstraintSystem_.addConstraint(a, name); +} + + +bool Protoboard::isSatisfied(Opcode opcode,const PrintOptions& printOnFail){ + if (opcode == Opcode::NONE){ + return constraintSystem_.isSatisfied(assignment_, printOnFail); + } + else{ + if (opcode == Opcode::MEMORY){ + return memoryConstraintSystem_.isSatisfied(assignment_, printOnFail); + } + else{ + int value = int(opcode); + return opcodeConstraintSystem_[value].isSatisfied(assignment_, printOnFail); + } + } + + +} + + + //Ariel: Changed implementation to vector of LC's for faster evaluation + void Protoboard::enforceBooleanity(const Algebra::Variable& var, Opcode opcode){ + Algebra::LinearCombination l1(Algebra::one() + var); + Polynomial constraint({ l1, Algebra::LinearCombination(var) }); + addGeneralConstraint(constraint, "" + var.name() + "* ( 1 + " + var.name() + ")", opcode); + + } + + +ConstraintSystem Protoboard::constraintSystem(Opcode opcode) const{ + if (opcode == Opcode::NONE){ + return constraintSystem_; + } + else if (opcode == Opcode::MEMORY){ + return memoryConstraintSystem_; + } + else{ + return opcodeConstraintSystem_[int(opcode)]; + } +} + +void Protoboard::multiplyConstraintSystem(Opcode opcode, const Algebra::CircuitPolynomial& mul){ + ConstraintSystem cs = constraintSystem(opcode); + for (Constraint constraint : cs.getConstraints()){ + addGeneralConstraint(mul * constraint.constraint(), constraint.asString() + "* selectorProgram", Opcode::NONE); + } +} + +void Protoboard::clearConstraintSystem(Opcode opcode){ + if (opcode == Opcode::NONE){ + constraintSystem_ = ConstraintSystem(); + } + else if(opcode ==Opcode::MEMORY){ + memoryConstraintSystem_ = ConstraintSystem(); + } + else { + opcodeConstraintSystem_[int(opcode)] = ConstraintSystem(); + } +} + +void Protoboard::setNewIndicesForTranslation(::std::vector translationVector){ + // Init new indecis + translation_ = translationVector; + for (unsigned int i = 0; i < translation_.size(); i++){ + if (i > 0 && (translation_[i - 1] == translation_[i])){ + translation_[i].setNewIndex(translation_[i - 1].getNewIndex()); + continue; + } + Algebra::Variable& v = translation_[i]; + v.setNewIndex(i); + old2newID_[v.getIndex()] = i; + + } + constraintSystem_.setNewIndices(old2newID_); + memoryConstraintSystem_.setNewIndices(old2newID_); +} + + +void Protoboard::addDegreeTranslation(const Algebra::FElem& elem, unsigned int degree){ + // Add assert that checks if g^degree = elem + degreeTranslation_.insert(std::pair(elem, degree)); +} + +unsigned int Protoboard::getDegreeOfFElem(const Algebra::FElem& elem){ + return degreeTranslation_.at(elem); +} + +void Protoboard::storeValue(const Algebra::FElem& address, const Algebra::FElem& value){ + memory_[address] = value; +} + +Algebra::FElem Protoboard::loadValue(const Algebra::FElem& address){ + std::map::iterator it = memory_.find(address); + GADGETLIB_ASSERT(it != memory_.end(), "Address(Memory) doesn't hold any value"); + return it->second; +} + +void Protoboard::addBoundaryConstraint(const Algebra::Variable& var, const size_t timeStamp, const Algebra::FElem& assignment){ + addBoundaryVariable(var); + addBoundaryTimestamp(timeStamp); + addBoundaryAssignment(assignment); +} + +///*************************************************************************************************/ +///*************************************************************************************************/ +///******************* ******************/ +///******************* class ProtoboardParams ******************/ +///******************* ******************/ +///*************************************************************************************************/ +///*************************************************************************************************/ + +ProtoboardParams::~ProtoboardParams() {} + +} // namespace gadgetlib diff --git a/tinyram/gadgetlib/gadgetlib/protoboard.hpp b/tinyram/gadgetlib/gadgetlib/protoboard.hpp new file mode 100644 index 0000000..b42854a --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/protoboard.hpp @@ -0,0 +1,184 @@ +/** @file + ***************************************************************************** + Definition of Protoboard, a "memory manager" for building arithmetic constraints + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef GADGETLIB3_PROTOBOARD_HPP_ +#define GADGETLIB3_PROTOBOARD_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT_CONSTRAINTS_SATISFIED(pb) \ + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)) + +#define ASSERT_CONSTRAINTS_NOT_SATISFIED(pb) \ + ASSERT_FALSE(pb->isSatisfied(PrintOptions::NO_DBG_PRINT)) + +namespace gadgetlib { + +class ProtoboardParams; // Forward declaration +class Protoboard; //Forward declaration + +typedef Algebra::CircuitPolynomial Polynomial; +typedef ::std::shared_ptr ProtoboardPtr; +typedef ::std::shared_ptr ProtoboardParamsCPtr; +typedef ::std::vector BoundaryVariables; +typedef ::std::vector BoundaryAssignments; +typedef ::std::vector BoundaryTimestamps; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class Protoboard ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class Protoboard{ +private: + ConstraintSystem memoryConstraintSystem_; // Memory Constratins. + ConstraintSystem constraintSystem_; // General Constraints + std::vector opcodeConstraintSystem_; // Constraints for each opcode + Algebra::VariableAssignment assignment_; + size_t numInputs_; + size_t numConstratins_; + ProtoboardParamsCPtr pParams_; // TODO try to refactor this out + + // We translate between my indices to Michaels + std::map old2newID_; + // When we transfer the variables to BREX we set them in a vector. + std::vector translation_; + // translation between g^i to i. + std::map degreeTranslation_; + // Simulate the code + std::vector memoryTrace_; + // Memory Implementaion - Mapping between an address and a value. + // Notice that for now we assume that each + std::map memory_; + // Boundary consistency + BoundaryVariables boundaryVariables_; + BoundaryAssignments boundaryAssignments_; + BoundaryTimestamps boundaryTimestamps_; // In some use cases, the same constraints system will be used + // many times while only one instance of verification should + // be checked for boundary conditions. In these cases we + // use boundaryTimestamps_ to specify which instance uses + // the boundary condition + + + + + Protoboard(ProtoboardParamsCPtr pParams) : + opcodeConstraintSystem_(33,ConstraintSystem()), + numInputs_(0), + numConstratins_(0), + pParams_(pParams) {}; + + void addBoundaryVariable(const Algebra::Variable& var) { boundaryVariables_.emplace_back(var); } + void addBoundaryTimestamp(const size_t timeStamp) { boundaryTimestamps_.emplace_back(timeStamp); } + void addBoundaryAssignment(const Algebra::FElem& assignment) { boundaryAssignments_.emplace_back(assignment); } + +public: + static ProtoboardPtr create(ProtoboardParamsCPtr pParams = NULL) { + return ProtoboardPtr(new Protoboard(pParams)); + } + unsigned int numConstraints() const; + Algebra::Variable::set getUsedVariables() const; + ProtoboardParamsCPtr params() const { return pParams_; } + Algebra::VariableAssignment assignment() const { return assignment_; } + Algebra::FElem& val(const Algebra::Variable& var); + const Algebra::FElem val(const Algebra::Variable& var) const; + const Algebra::FElem val(const Algebra::LinearCombination& lc) const; + void addGeneralConstraint(const Polynomial& a, + const ::std::string& name, + Opcode opcode); + void addMemoryConstraint(const Polynomial& a, + const std::string& name); + bool isSatisfied(Opcode opcode,const PrintOptions& printOnFail = PrintOptions::NO_DBG_PRINT); + void enforceBooleanity(const Algebra::Variable& var,Opcode opcode); + bool flagIsSet(const Algebra::FlagVariable& flag) const { return val(flag) == Algebra::one(); } + ConstraintSystem constraintSystem(Opcode opcode) const; + void multiplyConstraintSystem(Opcode opcode, const Algebra::CircuitPolynomial& mul); + void clearConstraintSystem(Opcode opcode); + void setNewIndicesForTranslation(::std::vector translationVector); + std::vector getTranslationVector() const { return translation_; } + void addDegreeTranslation(const Algebra::FElem& elem, unsigned int degree); + unsigned int getDegreeOfFElem(const Algebra::FElem& elem); + void addMemoryInfo(const MemoryInfo& memoryInfo){ memoryTrace_.emplace_back(memoryInfo); } + void storeValue(const Algebra::FElem& memory, const Algebra::FElem& value); + Algebra::FElem loadValue(const Algebra::FElem& address); + std::vector getMemoryTrace() { return memoryTrace_; } + void clearAssignment(){ assignment_.clear(); } + BoundaryVariables boundaryVariables() const { return boundaryVariables_; } + BoundaryAssignments boundaryAssignments() const { return boundaryAssignments_; } + BoundaryTimestamps boundaryTimestamps() const { return boundaryTimestamps_; } + void addBoundaryConstraint(const Algebra::Variable& var, const size_t timeStamp, const Algebra::FElem& assignment); +}; +/***********************************/ +/*** END OF CLASS DEFINITION ***/ +/***********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* class ProtoboardParams ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +/* + An abstract class to hold any additional information needed by a specific Protoboard. For + example a Protoboard specific to TinyRAM will have a class ArchParams which will inherit from + this class. +*/ +class ProtoboardParams { +public: + virtual ~ProtoboardParams() = 0; +}; + +///*************************************************************************************************/ +///*************************************************************************************************/ +///**************************** ****************************/ +///**************************** class TinyRAMProtoboardParams ****************************/ +///**************************** ****************************/ +///*************************************************************************************************/ +///*************************************************************************************************/ +// +//class TinyRAMProtoboardParams : public ProtoboardParams { +//private: +// TinyRAMArchParams archParams_; +// size_t opcodeWidth_; +// size_t timeBound_; +// size_t pcIncrement_; +//public: +// TinyRAMProtoboardParams(unsigned int numRegisters, unsigned int registerLength, +// size_t opcodeWidth, size_t timeBound, size_t pcIncrement) +// : archParams_(TinyRAMArchParams{ numRegisters, registerLength }), +// opcodeWidth_(opcodeWidth), +// timeBound_(timeBound), pcIncrement_(pcIncrement) {} +// TinyRAMProtoboardParams() +// : archParams_(TinyRAMArchParams{ 0, 0 }), opcodeWidth_(0), timeBound_(0), pcIncrement_(0) {} +// TinyRAMArchParams archParams() const { return archParams_; } +// size_t opcodeWidth() const { return opcodeWidth_; } +// size_t numRegisters() const { return archParams_.numRegisters; } +// size_t registerLength() const { return archParams_.registerLength; } +// size_t registerIndexLength() const { return Log2ceil(numRegisters()); } +// size_t arg2length() const { return std::max({ registerIndexLength(), registerLength() }); } +// size_t numOpcodes() const { return 1u << (opcodeWidth()); } +// size_t timeBound() const { return timeBound_; } +// size_t pcIncrement() const { return pcIncrement_; } +//}; // class TinyRAMProtoboardParams +// +} // namespace gadgetlib + + +#endif // LIBSNARK_GADGETLIB3_INCLUDE_GADGETLIB3_PROTOBOARD_HPP_ diff --git a/tinyram/gadgetlib/gadgetlib/tests/constraint_UTEST.cpp b/tinyram/gadgetlib/gadgetlib/tests/constraint_UTEST.cpp new file mode 100644 index 0000000..7b81166 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/tests/constraint_UTEST.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +using ::std::set; + +namespace PCP_Project { + +TEST(ConstraintsLib, Rank1Constraint) { + initPublicParamsFromEdwardsParam(); + //Rank1Constraint(const LinearCombination& a, + // const LinearCombination& b, + // const LinearCombination& c, + // const ::std::string& name); + VariableArray x(10,"x"); + VariableAssignment assignment; + for(int i = 0; i < 10; ++i) { + assignment[x[i]] = Fp(i); + } + LinearCombination a = x[0] + x[1] + 2; // = 0+1+2=3 + LinearCombination b = 2*x[2] - 3*x[3] + 4; // = 2*2-3*3+4=-1 + LinearCombination c = x[5]; // = 5 + Rank1Constraint c1(a,b,c,"c1"); + //LinearCombination a() const; + //LinearCombination b() const; + //LinearCombination c() const; + EXPECT_EQ(c1.a().eval(assignment), a.eval(assignment)); + EXPECT_EQ(c1.b().eval(assignment), b.eval(assignment)); + EXPECT_EQ(c1.c().eval(assignment), c.eval(assignment)); + //virtual bool isSatisfied(const VariableAssignment& assignment, bool printOnFail = false) const; + EXPECT_FALSE(c1.isSatisfied(assignment)); + EXPECT_FALSE(c1.isSatisfied(assignment, PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + assignment[x[5]] = -3; + EXPECT_TRUE(c1.isSatisfied(assignment)); + EXPECT_TRUE(c1.isSatisfied(assignment, PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + //virtual ::std::string annotation() const; --NOT TESTED, CAN CHANGE, FOR DEBUG ONLY + //const Variable::set getUsedVariables() const; + const Variable::set varSet = c1.getUsedVariables(); + EXPECT_EQ(varSet.size(), 5); + EXPECT_TRUE(varSet.find(x[0]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[1]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[2]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[3]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[4]) == varSet.end()); + EXPECT_TRUE(varSet.find(x[5]) != varSet.end()); +} + +TEST(ConstraintsLib, PolynomialConstraint) { + //PolynomialConstraint(const Polynomial& a, + // const Polynomial& b, + // const ::std::string& name); + VariableArray x(10,"x"); + VariableAssignment assignment; + for(int i = 0; i < 10; ++i) { + assignment[x[i]] = Algebra::one(); + } + Polynomial a = x[0] + x[1] + 1; // = 1+1+1=1 + Polynomial b = x[1]*x[2] - x[3] + 0; // = 1*1-1+0=0 + PolynomialConstraint c1(a,b,"c1"); + //virtual bool isSatisfied(const VariableAssignment& assignment, bool printOnFail = false) const; + EXPECT_FALSE(c1.isSatisfied(assignment)); + EXPECT_FALSE(c1.isSatisfied(assignment, PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + assignment[x[3]] = 0; + EXPECT_TRUE(c1.isSatisfied(assignment)); + EXPECT_TRUE(c1.isSatisfied(assignment, PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + //virtual ::std::string annotation() const; --NOT TESTED, CAN CHANGE, FOR DEBUG ONLY + //const Variable::set getUsedVariables() const; + const Variable::set varSet = c1.getUsedVariables(); + EXPECT_EQ(varSet.size(), 4); + EXPECT_TRUE(varSet.find(x[0]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[1]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[2]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[3]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[4]) == varSet.end()); +} + + +} // namespace PCP_Project diff --git a/tinyram/gadgetlib/gadgetlib/tests/gadget_UTEST.cpp b/tinyram/gadgetlib/gadgetlib/tests/gadget_UTEST.cpp new file mode 100644 index 0000000..ff74049 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/tests/gadget_UTEST.cpp @@ -0,0 +1,580 @@ +#include +#include +#include +#include +#include +#include + +using ::std::cerr; +using ::std::cout; +using ::std::endl; +using ::std::stringstream; + +#define EXHAUSTIVE_N 4 + +namespace PCP_Project { + +TEST(ConstraintsLib,R1P_AND_Gadget) { + initPublicParamsFromEdwardsParam(); + auto pb = Protoboard::create(R1P); + + VariableArray x(3, "x"); + Variable y("y"); + auto andGadget = AND_Gadget::create(pb, x, y); + andGadget->generateConstraints(); + + pb->val(x[0]) = 0; + pb->val(x[1]) = 1; + pb->val(x[2]) = 1; + andGadget->generateWitness(); + EXPECT_TRUE(pb->val(y) == 0); + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(y) = 1; + EXPECT_FALSE(pb->isSatisfied()); + + pb->val(x[0]) = 1; + andGadget->generateWitness(); + EXPECT_TRUE(pb->val(y) == 1); + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + + pb->val(y) = 0; + EXPECT_FALSE(pb->isSatisfied()); +} + +TEST(ConstraintsLib,LD2_AND_Gadget) { + auto pb = Protoboard::create(LD2); + + VariableArray x(3,"x"); + Variable y("y"); + auto andGadget = AND_Gadget::create(pb, x, y); + andGadget->generateConstraints(); + + pb->val(x[0]) = 0; + pb->val(x[1]) = 1; + pb->val(x[2]) = 1; + andGadget->generateWitness(); + EXPECT_TRUE(pb->val(y) == 0); + EXPECT_TRUE(pb->isSatisfied()); + pb->val(y) = 1; + EXPECT_FALSE(pb->isSatisfied()); + + pb->val(x[0]) = 1; + andGadget->generateWitness(); + EXPECT_TRUE(pb->val(y) == 1); + EXPECT_TRUE(pb->isSatisfied()); + + pb->val(y) = 0; + EXPECT_FALSE(pb->isSatisfied()); +} + + +void andGadgetExhaustiveTest(ProtoboardPtr pb, size_t n); // Forward declaration + +TEST(ConstraintsLib,R1P_ANDGadget_Exhaustive) { + initPublicParamsFromEdwardsParam(); + for(int n = 1; n <= EXHAUSTIVE_N; ++n) { + SCOPED_TRACE(FMT("n = %u \n", n)); + auto pb = Protoboard::create(R1P); + andGadgetExhaustiveTest(pb, n); + } +} + +TEST(ConstraintsLib,LD2_ANDGadget_Exhaustive) { + for(int n = 2; n <= EXHAUSTIVE_N; ++n) { + SCOPED_TRACE(FMT("n = %u \n", n)); + auto pb = Protoboard::create(LD2); + andGadgetExhaustiveTest(pb, n); + } +} + +TEST(ConstraintsLib,BinaryAND_Gadget) { + auto pb = Protoboard::create(LD2); + Variable input1("input1"); + Variable input2("input2"); + Variable result("result"); + auto andGadget = AND_Gadget::create(pb, input1, input2, result); + andGadget->generateConstraints(); + pb->val(input1) = pb->val(input2) = 0; + andGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_EQ(pb->val(result), 0); + pb->val(result) = 1; + ASSERT_FALSE(pb->isSatisfied()); + pb->val(result) = 0; + pb->val(input1) = 1; + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(input2) = 1; + ASSERT_FALSE(pb->isSatisfied()); + andGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_EQ(pb->val(result), 1); +} + +void orGadgetExhaustiveTest(ProtoboardPtr pb, size_t n); // Forward declaration + +TEST(ConstraintsLib,R1P_ORGadget_Exhaustive) { + initPublicParamsFromEdwardsParam(); + for(int n = 1; n <= EXHAUSTIVE_N; ++n) { + SCOPED_TRACE(FMT("n = %u \n", n)); + auto pb = Protoboard::create(R1P); + orGadgetExhaustiveTest(pb, n); + } +} + +TEST(ConstraintsLib,LD2_ORGadget_Exhaustive) { + for(int n = 2; n <= EXHAUSTIVE_N; ++n) { + SCOPED_TRACE(FMT("n = %u \n", n)); + auto pb = Protoboard::create(LD2); + orGadgetExhaustiveTest(pb, n); + } +} + +TEST(ConstraintsLib,BinaryOR_Gadget) { + auto pb = Protoboard::create(LD2); + Variable input1("input1"); + Variable input2("input2"); + Variable result("result"); + auto orGadget = OR_Gadget::create(pb, input1, input2, result); + orGadget->generateConstraints(); + pb->val(input1) = pb->val(input2) = 0; + orGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_EQ(pb->val(result), 0); + pb->val(result) = 1; + ASSERT_FALSE(pb->isSatisfied()); + pb->val(result) = 0; + pb->val(input1) = 1; + ASSERT_FALSE(pb->isSatisfied()); + pb->val(result) = 1; + ASSERT_CONSTRAINTS_SATISFIED(pb); + pb->val(input2) = 1; + orGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_EQ(pb->val(result), 1); +} + +TEST(ConstraintsLib,R1P_InnerProductGadget_Exhaustive) { + initPublicParamsFromEdwardsParam(); + const size_t n = EXHAUSTIVE_N; + auto pb = Protoboard::create(R1P); + VariableArray A(n, "A"); + VariableArray B(n, "B"); + Variable result("result"); + auto g = InnerProduct_Gadget::create(pb, A, B, result); + g->generateConstraints(); + for (size_t i = 0; i < 1u<val(A[k]) = i & (1u<val(B[k]) = j & (1u<generateWitness(); + EXPECT_EQ(pb->val(result) , FElem(correct)); + EXPECT_TRUE(pb->isSatisfied()); + // negative test + pb->val(result) = 100*n+19; + EXPECT_FALSE(pb->isSatisfied()); + } + } +} + +TEST(ConstraintsLib,LD2_InnerProductGadget_Exhaustive) { + initPublicParamsFromEdwardsParam(); + const size_t n = EXHAUSTIVE_N > 1 ? EXHAUSTIVE_N -1 : EXHAUSTIVE_N; + for(int len = 1; len <= n; ++len) { + auto pb = Protoboard::create(LD2); + VariableArray a(len, "a"); + VariableArray b(len, "b"); + Variable result("result"); + auto ipGadget = InnerProduct_Gadget::create(pb, a, b, result); + ipGadget->generateConstraints(); + // Generate Inputs & Witnesses + vec_GF2E a_vec; + a_vec.SetLength(len); + for(int h = 0; h < len; ++h) { // iterate over a's elements + for(int j = 0; j < 1u<val(a[h]) = to_GF2E(a_h); + vec_GF2E b_vec; + b_vec.SetLength(len); + for(int i = 0; i < len; ++i) { + pb->val(b[i]) = 0; + } + for(int i = 0; i < len; ++i) { // iterate over b's elements + for(int k = 0; k < 1u<val(b[i]) = to_GF2E(b_i); + ipGadget->generateWitness(); + GF2E resultGF2E; + InnerProduct(resultGF2E, a_vec, b_vec); + ::std::stringstream s; + s << endl << "i = " << i << endl + << "< " << a_vec << " > * < " << b_vec << " > = " << resultGF2E << endl + << pb->annotation(); + SCOPED_TRACE(s.str()); + EXPECT_EQ(pb->val(result), FElem(resultGF2E)); + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + // Negative test + pb->val(result) = resultGF2E + to_GF2E(1); + EXPECT_FALSE(pb->isSatisfied()); + } + } + } + } + } +} + +TEST(ConstraintsLib,R1P_LooseMUX_Gadget_Exhaustive) { +initPublicParamsFromEdwardsParam(); +const size_t n = EXHAUSTIVE_N; + auto pb = Protoboard::create(R1P); + VariableArray arr(1<generateConstraints(); + for (size_t i = 0; i < 1u<val(arr[i]) = (19*i) % (1u<val(index) = idx; + g->generateWitness(); + if (0 <= idx && idx <= (1<val(result) , (19*idx) % (1u<val(success_flag) , 1); + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(result) -= 1; + EXPECT_FALSE(pb->isSatisfied()); + } + else { + EXPECT_EQ(pb->val(success_flag) , 0); + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(success_flag) = 1; + EXPECT_FALSE(pb->isSatisfied()); + } + } +} + +// Forward declaration +void packing_Gadget_R1P_ExhaustiveTest(ProtoboardPtr unpackingPB, ProtoboardPtr packingPB, + const int n, VariableArray packed, VariableArray unpacked, + GadgetPtr packingGadget, GadgetPtr unpackingGadget); + +TEST(ConstraintsLib,R1P_Packing_Gadgets) { + initPublicParamsFromEdwardsParam(); + auto unpackingPB = Protoboard::create(R1P); + auto packingPB = Protoboard::create(R1P); + const int n = EXHAUSTIVE_N; + { // test CompressionPacking_Gadget + SCOPED_TRACE("testing CompressionPacking_Gadget"); + VariableArray packed(1, "packed"); + VariableArray unpacked(n, "unpacked"); + auto packingGadget = CompressionPacking_Gadget::create(packingPB, unpacked, packed, + PackingMode::PACK); + auto unpackingGadget = CompressionPacking_Gadget::create(unpackingPB, unpacked, packed, + PackingMode::UNPACK); + packing_Gadget_R1P_ExhaustiveTest(unpackingPB, packingPB, n, packed, unpacked, packingGadget, + unpackingGadget); + } + { // test IntegerPacking_Gadget + SCOPED_TRACE("testing IntegerPacking_Gadget"); + VariableArray packed(1, "packed"); + VariableArray unpacked(n, "unpacked"); + auto packingGadget = IntegerPacking_Gadget::create(packingPB, unpacked, packed, + PackingMode::PACK); + auto unpackingGadget = IntegerPacking_Gadget::create(unpackingPB, unpacked, packed, + PackingMode::UNPACK); + packing_Gadget_R1P_ExhaustiveTest(unpackingPB, packingPB, n, packed, unpacked, packingGadget, + unpackingGadget); + } +} + +TEST(ConstraintsLib,LD2_CompressionPacking_Gadget) { + auto unpackingPB = Protoboard::create(LD2); + auto packingPB = Protoboard::create(LD2); + const int n = EXHAUSTIVE_N; + VariableArray packed(1, "packed"); + VariableArray unpacked(n, "unpacked"); + auto packingGadget = CompressionPacking_Gadget::create(packingPB, unpacked, packed, + PackingMode::PACK); + auto unpackingGadget = CompressionPacking_Gadget::create(unpackingPB, unpacked, packed, + PackingMode::UNPACK); + packingGadget->generateConstraints(); + unpackingGadget->generateConstraints(); + for(int i = 0; i < 1u< bits(n); + size_t packedGF2X = 0; + for(int j = 0; j < n; ++j) { + bits[j] = i & 1u<val(unpacked[j]) = bits[j]; // set unpacked bits in the packing protoboard + } + // set the packed value in the unpacking protoboard + unpackingPB->val(packed[0]) = Algebra::mapIntegerToFieldElement(0,Algebra::ExtensionDegree,packedGF2X); + unpackingGadget->generateWitness(); + packingGadget->generateWitness(); + stringstream s; + s << endl << "i = " << i << ", Packed Value: " << Algebra::mapIntegerToFieldElement(0,Algebra::ExtensionDegree,packedGF2X) << endl; + SCOPED_TRACE(s.str()); + ASSERT_TRUE(unpackingPB->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_TRUE(packingPB->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + // check packed value is correct + ASSERT_EQ(packingPB->val(packed[0]), Algebra::mapIntegerToFieldElement(0,Algebra::ExtensionDegree,packedGF2X)); + for(int j = 0; j < n; ++j) { + // Tests for unpacking gadget + SCOPED_TRACE(FMT("\nValue being packed/unpacked: %u, bits[%u] = %u" , i, j, bits[j])); + ASSERT_EQ(unpackingPB->val(unpacked[j]), bits[j] ? 1 : 0); // check bit correctness + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = 1-bits[j]; // flip bit + ASSERT_FALSE(unpackingPB->isSatisfied()); + ASSERT_FALSE(packingPB->isSatisfied()); + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = bits[j]; // restore bit + // special case to test booleanity checks. Cause arithmetic constraints to stay + // satisfied while ruining Booleanity + if (j > 0 && bits[j]==1 && bits[j-1]==0 ) { + packingPB->val(unpacked[j-1]) = unpackingPB->val(unpacked[j-1]) = getGF2E_X(); + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = 0; + ASSERT_FALSE(unpackingPB->isSatisfied()); + ASSERT_TRUE(packingPB->isSatisfied()); // packing should not enforce Booleanity + // restore correct state + packingPB->val(unpacked[j-1]) = unpackingPB->val(unpacked[j-1]) = 0; + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = 1; + } + } + } + } + +TEST(ConstraintsLib, LD2_CompressionPacking_Gadget_largeNums) { + // Test packing/unpacking for the case of more than 1 packed element. + using ::std::vector; + + //initialize the context field + const int packedSize = 2; + const int unpackedSize = packedSize * IRR_DEGREE; + vector packingVal(packedSize); + for(int i = 0; i < packedSize; ++i) { + packingVal[i] = i; + } + packingVal[0] = 42; // The Answer To Life, the Universe and Everything + packingVal[1] = 26-9-1984; // My birthday + auto unpackingPB = Protoboard::create(LD2); + auto packingPB = Protoboard::create(LD2); + VariableArray packed(packedSize, "packed"); + VariableArray unpacked(unpackedSize, "unpacked"); + auto packingGadget = CompressionPacking_Gadget::create(packingPB, unpacked, packed, + PackingMode::PACK); + auto unpackingGadget = CompressionPacking_Gadget::create(unpackingPB, unpacked, packed, + PackingMode::UNPACK); + packingGadget->generateConstraints(); + unpackingGadget->generateConstraints(); + + vector bits(unpackedSize); + vector packedGF2X(packedSize,0); + for(int j = 0; j < unpackedSize; ++j) { + bits[j] = packingVal[j / IRR_DEGREE] & 1ul<<(j % IRR_DEGREE) ? 1 : 0; + packedGF2X[j / IRR_DEGREE] |= bits[j]<<(j % IRR_DEGREE); + packingPB->val(unpacked[j]) = bits[j]; // set unpacked bits in the packing protoboard + } + // set the packed value in the unpacking protoboard + for(int j = 0; j < packedSize; ++j) { + unpackingPB->val(packed[j]) = Algebra::mapIntegerToFieldElement(0,Algebra::ExtensionDegree,packedGF2X[j]); + } + unpackingGadget->generateWitness(); + packingGadget->generateWitness(); + ASSERT_TRUE(unpackingPB->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_TRUE(packingPB->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + // check packed values are correct + for(int j = 0; j < packedSize; ++j) { + ASSERT_EQ(packingPB->val(packed[j]), Algebra::mapIntegerToFieldElement(0,Algebra::ExtensionDegree,packedGF2X[j]); + } + for(int j = 0; j < unpackedSize; ++j) { + // Tests for unpacking gadget + SCOPED_TRACE(FMT("j = %lu", j)); + ASSERT_EQ(unpackingPB->val(unpacked[j]), bits[j] ? 1 : 0); // check bit correctness + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = 1-bits[j]; // flip bit + ASSERT_FALSE(unpackingPB->isSatisfied()); + ASSERT_FALSE(packingPB->isSatisfied()); + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = bits[j]; // restore bit + } +} + + +void equalsConstTest(ProtoboardPtr pb); // Forward declaration + +TEST(ConstraintsLib,R1P_EqualsConst_Gadget) { + initPublicParamsFromEdwardsParam(); + auto pb = Protoboard::create(R1P); + equalsConstTest(pb); +} + +TEST(ConstraintsLib,LD2_EqualsConst_Gadget) { + auto pb = Protoboard::create(LD2); + equalsConstTest(pb); +} + +TEST(ConstraintsLib,ConditionalFlag_Gadget) { + initPublicParamsFromEdwardsParam(); + auto pb = Protoboard::create(R1P); + FlagVariable flag; + Variable condition("condition"); + auto cfGadget = ConditionalFlag_Gadget::create(pb, condition, flag); + cfGadget->generateConstraints(); + pb->val(condition) = 1; + cfGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(condition) = 42; + cfGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_EQ(pb->val(flag),1); + pb->val(condition) = 0; + ASSERT_FALSE(pb->isSatisfied()); + cfGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_EQ(pb->val(flag),0); + pb->val(flag) = 1; + ASSERT_FALSE(pb->isSatisfied()); +} + +TEST(ConstraintsLib,LogicImplication_Gadget) { + auto pb = Protoboard::create(LD2); + FlagVariable flag; + Variable condition("condition"); + auto implyGadget = LogicImplication_Gadget::create(pb, condition, flag); + implyGadget->generateConstraints(); + pb->val(condition) = 1; + pb->val(flag) = 0; + ASSERT_FALSE(pb->isSatisfied()); + implyGadget->generateWitness(); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_EQ(pb->val(flag), 1); + pb->val(condition) = 0; + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + implyGadget->generateWitness(); + ASSERT_EQ(pb->val(flag), 1); + pb->val(flag) = 0; + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); +} + +void andGadgetExhaustiveTest(ProtoboardPtr pb, size_t n) { + VariableArray inputs(n, "inputs"); + Variable output("output"); + auto andGadget = AND_Gadget::create(pb, inputs, output); + andGadget->generateConstraints(); + for (size_t curInput = 0; curInput < 1u<val(inputs[maskBit]) = (curInput & (1u<generateWitness(); + { + SCOPED_TRACE(FMT("Positive (completeness) test failed. curInput: %u", curInput)); + EXPECT_TRUE(pb->isSatisfied()); + } + { + SCOPED_TRACE(pb->annotation()); + SCOPED_TRACE(FMT("Negative (soundness) test failed. curInput: %u, Constraints " + "are:", curInput)); + pb->val(output) = (curInput == ((1u<isSatisfied()); + } + } +} + +void orGadgetExhaustiveTest(ProtoboardPtr pb, size_t n) { + VariableArray inputs(n, "inputs"); + Variable output("output"); + auto orGadget = OR_Gadget::create(pb, inputs, output); + orGadget->generateConstraints(); + for (size_t curInput = 0; curInput < 1u<val(inputs[maskBit]) = (curInput & (1u<generateWitness(); + { + SCOPED_TRACE(FMT("Positive (completeness) test failed. curInput: %u", curInput)); + ASSERT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + } + { + SCOPED_TRACE(pb->annotation()); + SCOPED_TRACE(FMT("Negative (soundness) test failed. curInput: %u, Constraints " + "are:", curInput)); + pb->val(output) = (curInput == 0) ? 1 : 0; + ASSERT_FALSE(pb->isSatisfied()); + } + } +} + +void packing_Gadget_R1P_ExhaustiveTest(ProtoboardPtr unpackingPB, ProtoboardPtr packingPB, + const int n, VariableArray packed, VariableArray unpacked, + GadgetPtr packingGadget, GadgetPtr unpackingGadget) { + packingGadget->generateConstraints(); + unpackingGadget->generateConstraints(); + for(int i = 0; i < 1u< bits(n); + for(int j = 0; j < n; ++j) { + bits[j] = i & 1u<val(unpacked[j]) = bits[j]; // set unpacked bits in the packing protoboard + } + unpackingPB->val(packed[0]) = i; // set the packed value in the unpacking protoboard + unpackingGadget->generateWitness(); + packingGadget->generateWitness(); + ASSERT_TRUE(unpackingPB->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + ASSERT_TRUE(packingPB->isSatisfied()); + ASSERT_EQ(packingPB->val(packed[0]), i); // check packed value is correct + for(int j = 0; j < n; ++j) { + // Tests for unpacking gadget + SCOPED_TRACE(FMT("\nValue being packed/unpacked: %u, bits[%u] = %u" , i, j, bits[j])); + ASSERT_EQ(unpackingPB->val(unpacked[j]), bits[j]); // check bit correctness + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = 1-bits[j]; // flip bit + ASSERT_FALSE(unpackingPB->isSatisfied()); + ASSERT_FALSE(packingPB->isSatisfied()); + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = bits[j]; // restore bit + // special case to test booleanity checks. Cause arithmetic constraints to stay + // satisfied while ruining Booleanity + if (j > 0 && bits[j]==1 && bits[j-1]==0 ) { + packingPB->val(unpacked[j-1]) = unpackingPB->val(unpacked[j-1]) = 2; + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = 0; + ASSERT_FALSE(unpackingPB->isSatisfied()); + ASSERT_TRUE(packingPB->isSatisfied()); // packing should not enforce Booleanity + // restore correct state + packingPB->val(unpacked[j-1]) = unpackingPB->val(unpacked[j-1]) = 0; + packingPB->val(unpacked[j]) = unpackingPB->val(unpacked[j]) = 1; + } + } + } +} + +void equalsConstTest(ProtoboardPtr pb) { + Variable input("input"); + Variable result("result"); + auto gadget = EqualsConst_Gadget::create(pb, 0, input, result); + gadget->generateConstraints(); + pb->val(input) = 0; + gadget->generateWitness(); + // Positive test for input == n + EXPECT_EQ(pb->val(result), 1); + EXPECT_TRUE(pb->isSatisfied()); + // Negative test + pb->val(result) = 0; + EXPECT_FALSE(pb->isSatisfied()); + // Positive test for input != n + pb->val(input) = 1; + gadget->generateWitness(); + EXPECT_EQ(pb->val(result), 0); + EXPECT_TRUE(pb->isSatisfied()); + // Negative test + pb->val(input) = 0; + EXPECT_FALSE(pb->isSatisfied()); +} + + +} // namespace PCP_Project diff --git a/tinyram/gadgetlib/gadgetlib/tests/gadgetlib2_test.cpp b/tinyram/gadgetlib/gadgetlib/tests/gadgetlib2_test.cpp new file mode 100644 index 0000000..dca8f5d --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/tests/gadgetlib2_test.cpp @@ -0,0 +1,8 @@ +#include + +#define IRR_DEGREE 63 + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tinyram/gadgetlib/gadgetlib/tests/protoboard_UTEST.cpp b/tinyram/gadgetlib/gadgetlib/tests/protoboard_UTEST.cpp new file mode 100644 index 0000000..d75797b --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/tests/protoboard_UTEST.cpp @@ -0,0 +1,65 @@ +#include +#include +#include + +namespace PCP_Project { + +TEST(ConstraintsLib,R1P_enforceBooleanity) { + initPublicParamsFromEdwardsParam(); + auto pb = Protoboard::create(R1P); + Variable x; + pb->enforceBooleanity(x); + pb->val(x) = 0; + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(x) = 1; + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(x) = Fp(2); + EXPECT_FALSE(pb->isSatisfied()); +} + +TEST(ConstraintsLib,LD2_enforceBooleanity) { + auto pb = Protoboard::create(LD2); + Variable x; + pb->enforceBooleanity(x); + pb->val(x) = 0; + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + pb->val(x) = 1; + EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED)); + FieldElement x_squared = Algebra::mapIntegerToFieldElement(2,1,1); + pb->val(x) = x_squared; + EXPECT_FALSE(pb->isSatisfied()); +} + +TEST(ConstraintsLib, Protoboard_unpackedWordAssignmentEqualsValue_R1P) { + initPublicParamsFromEdwardsParam(); + auto pb = Protoboard::create(R1P); + const UnpackedWord unpacked(8, "unpacked"); + pb->setValuesAsBitArray(unpacked, 42); + ASSERT_TRUE(pb->unpackedWordAssignmentEqualsValue(unpacked, 42)); + ASSERT_FALSE(pb->unpackedWordAssignmentEqualsValue(unpacked, 43)); + ASSERT_FALSE(pb->unpackedWordAssignmentEqualsValue(unpacked, 1024 + 42)); +} + +TEST(ConstraintsLib, Protoboard_multipackedWordAssignmentEqualsValue_R1P) { + initPublicParamsFromEdwardsParam(); + auto pb = Protoboard::create(R1P); + const MultiPackedWord multipacked(8, R1P, "multipacked"); + pb->val(multipacked[0]) = 42; + ASSERT_TRUE(pb->multipackedWordAssignmentEqualsValue(multipacked, 42)); + ASSERT_FALSE(pb->multipackedWordAssignmentEqualsValue(multipacked, 43)); + const MultiPackedWord multipackedAgnostic(AGNOSTIC); + ASSERT_THROW(pb->multipackedWordAssignmentEqualsValue(multipackedAgnostic, 43), + ::std::runtime_error); +} + +TEST(ConstraintsLib, Protoboard_dualWordAssignmentEqualsValue_R1P) { + initPublicParamsFromEdwardsParam(); + auto pb = Protoboard::create(R1P); + const DualWord dualword(8, R1P, "dualword"); + pb->setDualWordValue(dualword, 42); + ASSERT_TRUE(pb->dualWordAssignmentEqualsValue(dualword, 42)); + ASSERT_FALSE(pb->dualWordAssignmentEqualsValue(dualword, 43)); + ASSERT_FALSE(pb->dualWordAssignmentEqualsValue(dualword, 42 + 1024)); +} + +} // namespace PCP_Project diff --git a/tinyram/gadgetlib/gadgetlib/tests/variable_UTEST.cpp b/tinyram/gadgetlib/gadgetlib/tests/variable_UTEST.cpp new file mode 100644 index 0000000..93550a3 --- /dev/null +++ b/tinyram/gadgetlib/gadgetlib/tests/variable_UTEST.cpp @@ -0,0 +1,550 @@ +#include +#include +#include +#include + +using ::std::set; + +namespace PCP_Project { + +TEST(ConstraintsLib, Variable) { + Variable v1; + EXPECT_EQ(v1.name(), ""); + Variable v2("foo"); +# ifdef DEBUG + EXPECT_EQ(v2.name(), "foo"); +# endif + Variable::VariableStrictOrder orderFunc; + EXPECT_TRUE(orderFunc(v1,v2) || orderFunc(v2,v1)); // check strict ordering + v2 = v1; + EXPECT_FALSE(orderFunc(v1,v2) || orderFunc(v2,v1)); + EXPECT_EQ(v2.name(), ""); + Variable::set s1; + s1.insert(v1); + s1.insert(v2); + EXPECT_EQ(s1.size(), 1); + Variable v3; + s1.insert(v3); + EXPECT_EQ(s1.size(), 2); + Variable v4; + s1.erase(v4); + EXPECT_EQ(s1.size(), 2); + v4 = v1; + s1.erase(v4); + EXPECT_EQ(s1.size(), 1); +} + +TEST(ConstraintsLib, VariableArray) { + Variable v1; + Variable v2("v2"); + VariableArray vArr; + vArr.push_back(v1); + vArr.push_back(v2); + EXPECT_EQ(vArr.size(),2); + Variable::VariableStrictOrder orderFunc; + EXPECT_TRUE(orderFunc(vArr[0],vArr[1]) || orderFunc(vArr[1],vArr[0])); // check strict ordering + vArr[1] = vArr[0]; + EXPECT_FALSE(orderFunc(vArr[0],vArr[1]) || orderFunc(vArr[1],vArr[0])); // check strict ordering + EXPECT_THROW(vArr.at(2) = v1, ::std::out_of_range); +} + +TEST(ConstraintsLib, FElem_FConst) { + initPublicParamsFromEdwardsParam(); + //FElem(const long n); + FElem e0(long(0)); + EXPECT_TRUE(e0 == 0); + //FElem(const int i); + FElem e1(int(1)); + EXPECT_TRUE(e1 == 1); + FElem e2(2); + EXPECT_EQ(e2, FElem(2)); + //FElem(const FElem& src); + FElem e3(e1); + EXPECT_TRUE(e3 == 1); + e3 = 0; + EXPECT_TRUE(e3 == 0); + ASSERT_EQ(e1, 1); + e0 = e1; + EXPECT_EQ(e0, e1); + e0 = 0; + EXPECT_NE(e0, e1); + //FElem& operator=(const FElem&& other); + FElem e4(FElem(4)); + EXPECT_EQ(e4, FElem(4)); + //FElem& operator=(const FElem& other); + //FElem& operator=(const long i) { *elem_ = i; return *this;} + e0 = e1 = 42; + EXPECT_EQ(e1, FElem(42)); + EXPECT_EQ(e0, e1); + //::std::string asString() const {return elem_->asString();} + #ifdef DEBUG + EXPECT_EQ(e0.asString(),"42"); + #else // ifdef DEBUG + EXPECT_EQ(e0.asString(),""); + #endif // ifdef DEBUG + //FieldType fieldType() const; + EXPECT_EQ(e0.fieldType(), AGNOSTIC); + //bool operator==(const FElem& other) const {return *elem_ == *other.elem_;} + EXPECT_TRUE(e1 == e0); + EXPECT_FALSE(e1 == e4); + FElem eR1P = Fp(42); + EXPECT_TRUE(e1 == eR1P); + EXPECT_TRUE(eR1P == e1); + //FElem& operator+=(const FElem& other) {*elem_ += *other.elem_; return *this;} + e1 = e0 += e1; + EXPECT_EQ(e0, FElem(84)); + EXPECT_TRUE(e1 == 84); + //FElem& operator-=(const FElem& other) {*elem_ -= *other.elem_; return *this;} + e1 = e0 -= e1; + EXPECT_TRUE(e0 == 0); + EXPECT_TRUE(e1 == 0); + e0 = 21; + e1 = 2; + EXPECT_EQ(e0,FElem(21)); + EXPECT_TRUE(e1 == 2); + //FElem& operator*=(const FElem& other) {*elem_ *= *other.elem_; return *this;} + e1 = e0 *= e1; + EXPECT_TRUE(e0 == 42); + EXPECT_TRUE(e1 == 42); + EXPECT_TRUE(e0 == e1); + EXPECT_TRUE(e0 == 42); + EXPECT_TRUE(42 == e0); + //FElem operator-() const; + EXPECT_EQ(-e4, FElem(-4)); + //bool operator!=(const FElem& first, const FElem& second); + EXPECT_TRUE(e4 != e0); + //bool operator==(const FElem& first, const long second); + EXPECT_TRUE(e4 == 4); + //bool operator==(const long first, const FElem& second); + EXPECT_TRUE(4 == e4); + //bool operator!=(const FElem& first, const long second); + EXPECT_TRUE(e4 != 5); + //bool operator!=(const long first, const FElem& second); + EXPECT_TRUE(5 != e4); + //FElem inverse() const; + e1 = e4.inverse(R1P); + EXPECT_EQ(e1, FElem(Fp(e4.asLong()).inverse())); +} + +TEST(ConstraintsLib, FElem_R1P_Elem) { + initPublicParamsFromEdwardsParam(); + //FElem(const Fp& elem); + FElem e0(Fp(0)); + EXPECT_EQ(e0,0); + EXPECT_NE(e0,1); + //FElem(const FElem& src); + FElem e1(e0); + EXPECT_EQ(e1,0); + //FElem& operator=(const FElem& other); + FElem e2 = Fp(2); + e1 = e2; + EXPECT_EQ(e1,2); + FElem e3 = 3; + e1 = e3; + EXPECT_EQ(e1,3); + //FElem& operator=(const FElem&& other); + e1 = FElem(Fp(2)); + EXPECT_EQ(e1,2); + e1 = FElem(1); + EXPECT_EQ(e1,1); + //FElem& operator=(const long i) { *elem_ = i; return *this;} + e1 = long(42); + EXPECT_EQ(e1,42); + //::std::string asString() const {return elem_->asString();} + #ifdef DEBUG + EXPECT_EQ(e1.asString(),"42"); + #else // ifdef DEBUG + EXPECT_EQ(e1.asString(),""); + #endif // ifdef DEBUG + //FieldType fieldType() const; + EXPECT_EQ(e1.fieldType(),R1P); + //bool operator==(const FElem& other) const {return *elem_ == *other.elem_;} + e0 = 42; + EXPECT_TRUE(e0 == e1); + EXPECT_FALSE(e0 == e2); + EXPECT_FALSE(e0 != e1); + EXPECT_TRUE(e0 == 42); + EXPECT_FALSE(e0 == 41); + EXPECT_TRUE(e0 != 41); + EXPECT_TRUE(42 == e0); + EXPECT_TRUE(41 != e0); + // negative numbers + e0 = e1 = -42; + EXPECT_TRUE(e0 == e1); + EXPECT_FALSE(e0 == e2); + EXPECT_FALSE(e0 != e1); + EXPECT_TRUE(e0 == -42); + EXPECT_FALSE(e0 == -41); + EXPECT_TRUE(e0 != -41); + EXPECT_TRUE(-42 == e0); + EXPECT_TRUE(-41 != e0); + //FElem& operator*=(const FElem& other) {*elem_ *= *other.elem_; return *this;} + e0 = 0; + e1 = 1; + e2 = 2; + e3 = 3; + EXPECT_TRUE(e0.fieldType() == R1P && e1.fieldType() == R1P && e2.fieldType() == R1P); + e1 = e2 *= e3; + EXPECT_EQ(e1,6); + EXPECT_EQ(e2,6); + EXPECT_EQ(e3,3); + ////FElem& operator+=(const FElem& other) {*elem_ += *other.elem_; return *this;} + e1 = e2 += e3; + EXPECT_EQ(e1,9); + EXPECT_EQ(e2,9); + EXPECT_EQ(e3,3); + //FElem& operator-=(const FElem& other) {*elem_ -= *other.elem_; return *this;} + e1 = e2 -= e3; + EXPECT_EQ(e1,6); + EXPECT_EQ(e2,6); + EXPECT_EQ(e3,3); + //FElem operator-() const; + e3 = -e2; + EXPECT_EQ(e2,6); + EXPECT_EQ(e3,-6); + EXPECT_TRUE(e3.fieldType() == R1P); + //FElem inverse() const; + FElem e42 = Fp(42); + EXPECT_EQ(e42.inverse(R1P),Fp(42).inverse()); +} + + +TEST(ConstraintsLib, FElem_LD2_Elem) { + initPublicParamsFromEdwardsParam(); + //FElem(const GF2E& elem); + FElem e0(Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,0)); + EXPECT_EQ(e0,0); + EXPECT_NE(e0,1); + //FElem(const FElem& src); + FElem e1(e0); + EXPECT_EQ(e1,0); + //FElem& operator=(const FElem& other); + FElem e2 = Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,1); + e1 = e2; + EXPECT_EQ(e1,1); + FElem e3 = 0; + e1 = e3; + EXPECT_EQ(e1,0); + e3 = 3; + EXPECT_ANY_THROW(e1 = e3;); + //FElem& operator=(const FElem&& other); + e1 = FElem(Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,1)); + EXPECT_EQ(e1,1); + e1 = FElem(0); + EXPECT_EQ(e1,0); + //FElem& operator=(const long i) { *elem_ = i; return *this;} + e1 = long(1); + EXPECT_EQ(e1,1); + EXPECT_ANY_THROW(e1 = long(42)); + //::std::string asString() const {return elem_->asString();} + EXPECT_EQ(e1.asString(),"[1]"); + e1 = 0; + EXPECT_EQ(e1.asString(),"[]"); + //FieldType fieldType() const; + EXPECT_EQ(e1.fieldType(),LD2); + //bool operator==(const FElem& other) const {return *elem_ == *other.elem_;} + e0 = 0; + EXPECT_TRUE(e0 == e1); + EXPECT_FALSE(e0 == e2); + EXPECT_FALSE(e0 != e1); + EXPECT_TRUE(e0 == 0); + EXPECT_FALSE(e0 == 1); + EXPECT_TRUE(e0 != 1); + EXPECT_TRUE(0 == e0); + EXPECT_TRUE(1 != e0); + //FElem& operator*=(const FElem& other) {*elem_ *= *other.elem_; return *this;} + FieldElement g0 = mapIntegerToFieldElement(0,Algebra::ExtensionDegree,1); + FieldElement g1 = mapIntegerToFieldElement(0,Algebra::ExtensionDegree,2); + FieldElement g2 = mapIntegerToFieldElement(0,Algebra::ExtensionDegree,4); + FieldElement g3 = mapIntegerToFieldElement(0,Algebra::ExtensionDegree,6); + e0 = Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,g0); + e1 = Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,g1); + e2 = Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,g2); + e3 = Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,g3); + EXPECT_EQ(e0.asString(),"[1]"); + EXPECT_EQ(e1.asString(),"[0 1]"); + EXPECT_EQ(e2.asString(),"[0 0 1]"); + EXPECT_EQ(e3.asString(),"[0 1 1]"); + e1 = e2 *= e3; + EXPECT_EQ(e1.asString(),"[0 0 0 1 1]"); + EXPECT_EQ(e2.asString(),"[0 0 0 1 1]"); + EXPECT_EQ(e3.asString(),"[0 1 1]"); + //FElem& operator+=(const FElem& other) {*elem_ += *other.elem_; return *this;} + e1 = e2 += e3; + EXPECT_EQ(e1.asString(),"[0 1 1 1 1]"); + EXPECT_EQ(e2.asString(),"[0 1 1 1 1]"); + EXPECT_EQ(e3.asString(),"[0 1 1]"); + //FElem& operator-=(const FElem& other) {*elem_ -= *other.elem_; return *this;} + e1 = e2 -= e3; + EXPECT_EQ(e1.asString(),"[0 0 0 1 1]"); + EXPECT_EQ(e2.asString(),"[0 0 0 1 1]"); + EXPECT_EQ(e3.asString(),"[0 1 1]"); + //FElem operator-() const; + e3 = -e2; + EXPECT_EQ(e2.asString(),"[0 0 0 1 1]"); + EXPECT_EQ(e3.asString(),"[0 0 0 1 1]"); + //FElem inverse() const; + e3 = e3 = Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,g3); + EXPECT_EQ(e3.inverse(LD2), FElem(inv(Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,g3)))); + // Tests for added functionality, creating LD2_Elem from constant -1 + FElem eNeg1(-1); + EXPECT_EQ(eNeg1, -1); + eNeg1 = 0; + EXPECT_NE(eNeg1, -1); + eNeg1 = -1; + EXPECT_TRUE(eNeg1 == -1); +} + +TEST(ConstraintsLib, LinearTerm) { + initPublicParamsFromEdwardsParam(); + //LinearTerm(const Variable& v) : variable_(v), coeff_(1) {} + VariableArray x(10, "x"); + LinearTerm lt0(x[0]); + VariableAssignment ass; + ass[x[0]] = Fp(42); + EXPECT_EQ(x[0].eval(ass), 42); + EXPECT_NE(x[0].eval(ass), 17); + EXPECT_EQ(lt0.eval(ass), 42); + EXPECT_NE(lt0.eval(ass), 17); + ass[x[0]] = Fp(2); + EXPECT_EQ(lt0.eval(ass), 2); + LinearTerm lt1(x[1]); + ass[x[1]] = Algebra::mainIntegerToFieldElement(0,Algebra::ExtensionDegree,1); + EXPECT_EQ(lt1.eval(ass), 1); + LinearTerm lt2(x[2]); + ass[x[2]] = 24; + EXPECT_EQ(lt2.eval(ass), 24); + //LinearTerm(const Variable& v, const FElem& coeff) : variable_(v), coeff_(coeff) {} + LinearTerm lt3(x[3], Fp(3)); + ass[x[3]] = Fp(4); + EXPECT_EQ(lt3.eval(ass), 3*4); + LinearTerm lt4(x[4], Fp(4)); + ass[x[4]] = Algebra::mapIntegerToFieldElement(0,Algebra::ExtensionDegree,4); + EXPECT_ANY_THROW((lt4.eval(ass), 3*7)); // Mixed types + //LinearTerm(const Variable& v, long n) : variable_(v), coeff_(n) {} + LinearTerm lt5(x[5], long(2)); + ass[x[5]] = 5; + EXPECT_EQ(lt5.eval(ass), 5*2); + LinearTerm lt6(x[6], 2); + ass[x[6]] = 6; + EXPECT_EQ(lt6.eval(ass), 6*2); + //LinearTerm operator-() const {return LinearTerm(variable_, -coeff_);} + LinearTerm lt7 = -lt6; + EXPECT_EQ(lt7.eval(ass), -6*2); + //virtual FieldType fieldtype() const {return coeff_.fieldType();} + EXPECT_EQ(lt6.fieldtype(), AGNOSTIC); + EXPECT_EQ(lt3.fieldtype(), R1P); + LinearTerm lt8(x[8], Algebra::one()); + EXPECT_EQ(lt8.fieldtype(), LD2); + + //virtual ::std::string asString() const; + // R1P +#ifdef DEBUG + LinearTerm lt10(x[0], Fp(-1)); + EXPECT_EQ(lt10.asString(), "-1 * x[0]"); + LinearTerm lt11(x[0], Fp(0)); + EXPECT_EQ(lt11.asString(), "0 * x[0]"); + LinearTerm lt12(x[0], Fp(1)); + EXPECT_EQ(lt12.asString(), "x[0]"); + LinearTerm lt13(x[0], Fp(2)); + EXPECT_EQ(lt13.asString(), "2 * x[0]"); + // LD2 + LinearTerm lt21(x[0], Algebra::zero()); + EXPECT_EQ(lt21.asString(), "0 * x[0]"); + LinearTerm lt22(x[0], Algebra::one()); + EXPECT_EQ(lt22.asString(), "x[0]"); + LinearTerm lt23(x[0], Algebra::xFE()); + EXPECT_EQ(lt23.asString(), "[0 1] * x[0]"); + // AGNOSTIC + LinearTerm lt30(x[0], -1); + EXPECT_EQ(lt30.asString(), "-1 * x[0]"); + LinearTerm lt31(x[0], 0); + EXPECT_EQ(lt31.asString(), "0 * x[0]"); + LinearTerm lt32(x[0], Fp(1)); + EXPECT_EQ(lt32.asString(), "x[0]"); + LinearTerm lt33(x[0], Fp(2)); + EXPECT_EQ(lt33.asString(), "2 * x[0]"); +#endif // DEBUG + // LinearTerm& operator*=(const FElem& other); + LinearTerm lt42(x[0], Fp(1)); + LinearTerm lt43(x[0], Fp(2)); + lt42 = lt43 *= FElem(4); + EXPECT_EQ(lt42.eval(ass), 8*2); + EXPECT_EQ(lt43.eval(ass), 8*2); +} + +TEST(ConstraintsLib, LinearCombination) { + initPublicParamsFromEdwardsParam(); +// LinearCombination() : linearTerms_(), constant_(0) {} + LinearCombination lc0; + VariableAssignment assignment; + EXPECT_EQ(lc0.eval(assignment),0); +// LinearCombination(const Variable& var) : linearTerms_(1,var), constant_(0) {} + VariableArray x(10,"x"); + LinearCombination lc1(x[1]); + assignment[x[1]] = 42; + EXPECT_EQ(lc1.eval(assignment),42); +// LinearCombination(const LinearTerm& linTerm) : linearTerms_(1,linTerm), constant_(0) {} + LinearTerm lt(x[2], Fp(2)); + LinearCombination lc2 = lt; + assignment[x[2]] = 2; + EXPECT_EQ(lc2.eval(assignment),4); +// LinearCombination(long i) : linearTerms_(), constant_(i) {} + LinearCombination lc3 = 3; + EXPECT_EQ(lc3.eval(assignment),3); +// LinearCombination(const FElem& elem) : linearTerms_(), constant_(elem) {} + FElem elem = Fp(4); + LinearCombination lc4 = elem; + EXPECT_EQ(lc4.eval(assignment),4); +// LinearCombination& operator+=(const LinearCombination& other); + lc1 = lc4 += lc2; + EXPECT_EQ(lc4.eval(assignment),4+4); + EXPECT_EQ(lc1.eval(assignment),4+4); + EXPECT_EQ(lc2.eval(assignment),4); +// LinearCombination& operator-=(const LinearCombination& other); + lc1 = lc4 -= lc3; + EXPECT_EQ(lc4.eval(assignment),4+4-3); + EXPECT_EQ(lc1.eval(assignment),4+4-3); + EXPECT_EQ(lc3.eval(assignment),3); +// ::std::string asString() const; +# ifdef DEBUG + EXPECT_EQ(lc1.asString(), "2 * x[2] + 1"); +# else // ifdef DEBUG + EXPECT_EQ(lc1.asString(), ""); +# endif // ifdef DEBUG +// Variable::set getUsedVariables() const; + Variable::set sVar = lc1.getUsedVariables(); + EXPECT_EQ(sVar.size(),1); + assignment[x[2]] = 83; + EXPECT_EQ(assignment[*sVar.begin()], 83); + assignment[x[2]] = 2; +// LinearCombination operator-(const LinearCombination& lc); + lc2 = -lc1; + EXPECT_EQ(lc2.eval(assignment),-5); + lc2 = lc1 *= FElem(4); + EXPECT_EQ(lc1.eval(assignment),5*4); + EXPECT_EQ(lc2.eval(assignment),5*4); +} + +TEST(ConstraintsLib, Monomial) { + initPublicParamsFromEdwardsParam(); + //Monomial(const Variable& var) : coeff_(1), variables_(1, var) {} + //FElem eval(const VariableAssignment& assignment) const; + VariableArray x(10, "x"); + Monomial m0 = x[0]; + VariableAssignment assignment; + assignment[x[0]] = 42; + EXPECT_EQ(m0.eval(assignment), 42); + //Monomial(const Variable& var, const FElem& coeff) : coeff_(coeff), variables_(1, var) {} + Monomial m1(x[1], Fp(3)); + assignment[x[1]] = 2; + EXPECT_EQ(m1.eval(assignment), 6); + //Monomial(const FElem& val) : coeff_(val), variables_() {} + Monomial m2 = Monomial(Algebra::one()); + EXPECT_EQ(m2.eval(assignment), 1); + //Monomial(const LinearTerm& linearTerm); + LinearTerm lt(x[3], 3); + Monomial m3 = lt; + assignment[x[3]] = 3; + EXPECT_EQ(m3.eval(assignment), 9); + //Monomial operator-() const; + Monomial m4 = -m3; + EXPECT_EQ(m4.eval(assignment), -9); + //Monomial& operator*=(const Monomial& other); + m3 = m4 *= m0; + EXPECT_EQ(m3.eval(assignment), -9 * 42); + EXPECT_EQ(m4.eval(assignment), -9 * 42); + EXPECT_EQ(m0.eval(assignment), 42); + //Variable::set getUsedVariables() const; + Variable::set varSet = m3.getUsedVariables(); + ASSERT_EQ(varSet.size(), 2); + EXPECT_TRUE(varSet.find(x[0]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[3]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[4]) == varSet.end()); + //::std::string asString() const +# ifdef DEBUG + EXPECT_EQ(m3.asString(), "-3*x[0]*x[3]"); +# else + EXPECT_EQ(m3.asString(), ""); +# endif +} + + +TEST(ConstraintsLib, Polynomial) { + initPublicParamsFromEdwardsParam(); + VariableArray x(10,"x"); + VariableAssignment assignment; + //Polynomial() : monomials_(), constant_(0) {} + //FElem eval(const VariableAssignment& assignment) const; + Polynomial p0; + EXPECT_EQ(p0.eval(assignment), 0); + //Polynomial(const Monomial& monomial) : monomials_(1, monomial), constant_(0) {} + Monomial m0(x[0], 3); + Polynomial p1 = m0; + assignment[x[0]] = 2; + EXPECT_EQ(p1.eval(assignment), 6); + //Polynomial(const Variable& var) : monomials_(1, Monomial(var)), constant_(0) {} + Polynomial p2 = x[2]; + assignment[x[2]] = 2; + EXPECT_EQ(p2.eval(assignment), 2); + //Polynomial(const FElem& val) : constant_(val), monomials_() {} + Polynomial p3 = FElem(Fp(3)); + EXPECT_EQ(p3.eval(assignment), 3); + //Polynomial(const LinearCombination& linearCombination); + LinearCombination lc(x[0]); + lc += x[2]; + Polynomial p4 = lc; + EXPECT_EQ(p4.eval(assignment), 4); + //Variable::set getUsedVariables() const; + const Variable::set varSet = p4.getUsedVariables(); + EXPECT_EQ(varSet.size(), 2); + EXPECT_TRUE(varSet.find(x[0]) != varSet.end()); + EXPECT_TRUE(varSet.find(x[2]) != varSet.end()); + //Polynomial& operator+=(const Polynomial& other); + Polynomial p5 = p4 += p3; + EXPECT_EQ(p5.eval(assignment), 7); + //::std::string asString() const; +# ifdef DEBUG + EXPECT_EQ(p0.asString(), "0"); + EXPECT_EQ(p1.asString(), "3*x[0]"); + EXPECT_EQ(p2.asString(), "x[2]"); + EXPECT_EQ(p3.asString(), "3"); + EXPECT_EQ(p4.asString(), "x[0] + x[2] + 3"); + EXPECT_EQ(p5.asString(), "x[0] + x[2] + 3"); +# endif // DEBUG + //Polynomial& operator*=(const Polynomial& other); + p0 = p4 *= p5; + EXPECT_EQ(p0.eval(assignment), 7*7); + EXPECT_EQ(p4.eval(assignment), 7*7); + EXPECT_EQ(p5.eval(assignment), 7); + //Polynomial& operator-=(const Polynomial& other); + p0 = p4 -= p1; + EXPECT_EQ(p0.eval(assignment), 7*7-6); + EXPECT_EQ(p4.eval(assignment), 7*7-6); + EXPECT_EQ(p1.eval(assignment), 6); + //inline Polynomial operator-(const Polynomial& src) {return Polynomial(FElem(0)) -= src;} + p2 = -p0; + EXPECT_EQ(p0.eval(assignment), 7*7-6); + EXPECT_EQ(p2.eval(assignment), -(7*7-6)); +} + +// +//#ifdef DEBUG +//TEST(ConstraintsLib,R1P_annotations) +//{ +// R1P_Variable v("v"); +// R1P_LinearTerm lt(v); +// EXPECT_EQ(lt.annotation(), "v"); +// lt = 5 * lt; +// EXPECT_EQ(lt.annotation(), "5*v"); +// R1P_Protoboard pb; +// R1P_VariableArray arr = R1P_VariableArray::create(&pb,5,"a"); +// R1P_LinearCombination lc(lt + arr[0] + 2*arr[3] + 3*arr[4]*2); +// EXPECT_EQ(lc.annotation(), "5*v + a[0] + 2*a[3] + 6*a[4]"); +// Rank1Constraint c(lc,v,lt, "lc*v = lt"); +// EXPECT_EQ(c.annotation(), "( 5*v + a[0] + 2*a[3] + 6*a[4] ) * ( v ) = 5*v"); +//} +//#endif + + +} // namespace PCP_Project diff --git a/tinyram/stark-tinyram/Makefile b/tinyram/stark-tinyram/Makefile new file mode 100644 index 0000000..8e20fec --- /dev/null +++ b/tinyram/stark-tinyram/Makefile @@ -0,0 +1,51 @@ +CC=g++ +CPPFLAGS=-std=c++14 +CFLAGS=-O3 -g -Wall -fmessage-length=0 -fopenmp -mavx -maes -mtune=native + +INCFLAGS=-Isrc -I$(GADGET3INC) -I$(LIBSTARKINC) -I$(ALGEBRAINC) -I$(FFTINC) +LIBS= \ + gadgetlib \ + stark \ + algebralib \ + FFT + +LIBFLAGS=$(addprefix -l, $(LIBS)) +LNKFLAGS=-L"$(ALGEBRALNKDIR)" -L"$(FFTLIBLNKDIR)" -L"$(GADGET3LNKDIR)" -L"$(LIBSTARKLINKDIR)" -lgomp + +SRCDIR = src +SRCEXT = cpp +OBJDIR = $(BLDDIR)/obj + +SRCS := $(shell find $(SRCDIR) -name '*.$(SRCEXT)') +SRCDIRS := $(shell find . -name '*.$(SRCEXT)' -exec dirname {} \; | uniq) +OBJS := $(patsubst %.$(SRCEXT),$(OBJDIR)/%.o,$(SRCS)) + +TARGET=$(BLDDIR)/stark-tinyram +DST=$(EXEDIR)/stark-tinyram +all: $(TARGET) + +$(TARGET): buildrepo $(OBJS) +# @echo 'Building target: $@' +# @echo 'Invoking: GCC Linker' + $(CC) -o "$@" $(OBJS) -fopenmp $(LNKFLAGS) $(LIBFLAGS) +# @echo 'Finished building target: $@' + cp $(TARGET) $(DST) + +$(OBJDIR)/%.o: %.$(SRCEXT) +# @echo "$(CC) $(CFLAGS) $(CPPFLAGS) $(INCFLAGS) -c -o "$@" "$<"" + $(CC) $(CFLAGS) $(CPPFLAGS) $(INCFLAGS) -c -o "$@" "$<" + +clean: + rm -f $(TARGET) $(OBJS) $(DEPS) + rm -rf $(OBJDIR) + rm $(DST) + +buildrepo: + $(call make-repo) + +define make-repo +for dir in $(SRCDIRS); \ + do \ + mkdir -p $(OBJDIR)/$$dir; \ + done +endef diff --git a/tinyram/stark-tinyram/src/TinyRAM/TinyRAMDefinitions.cpp b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMDefinitions.cpp new file mode 100644 index 0000000..aff0824 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMDefinitions.cpp @@ -0,0 +1,58 @@ +#include "TinyRAMDefinitions.hpp" + + + +int trNumRegisters = -1; +int trRegisterLen = -1; +int trNumRegistersInclPC = -1; +int trRegisterOrPCIndexLen = -1; +int trRegisterNoPCIndexLen = -1; +int trRegisterIndexLen = -1; +int trPCLength = -1; +int trInstructionLength = -1; +int trConfigurationLength = -1; +int trFirstReadInstr = -1; +int trInputReadSteps = -1; +int trInputRegisterNum = -1; + +static bool trParametersInited = false; + +void clearTinyRAMParams(){ + trParametersInited = false; + trNumRegisters = -1; + trRegisterLen = -1; + trNumRegistersInclPC = -1; + trRegisterOrPCIndexLen = -1; + trRegisterNoPCIndexLen = -1; + trRegisterIndexLen = -1; + trPCLength = -1; + trInstructionLength = -1; + trConfigurationLength = -1; + trFirstReadInstr = -1; + trInputReadSteps = -1; + trInputRegisterNum = -1; +} + +void initTinyRAMParams(int numRegisters, int registerLen) { + trParametersInited = true; + + trNumRegisters = numRegisters; + trRegisterLen = registerLen; + trNumRegistersInclPC = trNumRegisters + 1; + trRegisterOrPCIndexLen = (int)ceil( ::Infrastructure::Log2((double)(trNumRegistersInclPC)) ); + trRegisterNoPCIndexLen = (int)ceil( ::Infrastructure::Log2((double)(trNumRegisters)) ); + trRegisterIndexLen = trRegisterOrPCIndexLen; + trPCLength = trRegisterLen; + trInstructionLength = trOpcodeLen + 1 + 2*trRegisterIndexLen + max(trRegisterIndexLen,trRegisterLen); + trConfigurationLength = trPCLength + trNumRegisters * trRegisterLen + 1; + + //Input Reading: + trFirstReadInstr = 1; + trInputReadSteps = 3; + trInputRegisterNum = 0; +} + +void initTinyRAMParamsFromEnvVariables() { + initTinyRAMParams(REGISTERS_NUMBER, + REGISTER_LENGTH); +} diff --git a/tinyram/stark-tinyram/src/TinyRAM/TinyRAMDefinitions.hpp b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMDefinitions.hpp new file mode 100644 index 0000000..d286d38 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMDefinitions.hpp @@ -0,0 +1,74 @@ +#ifndef RAM_DEFINITIONS_HPP +#define RAM_DEFINITIONS_HPP +/************************************************************************TinyRAMDefinitions.hpp****************************************/ +/** @file + * Basic definitions of parameters and constants for TinyRAM, together with data types needed in the reductions from TinyRAM. + * It also contains functions for creating input instances for the RAMParams constructor. + **/ + +#include +#include +#include +#include +#include + +#define REGISTERS_NUMBER 15 +#define REGISTER_LENGTH 16 +#define NUMBER_OPCODES 32 +#define BIN_EOF_STRING "EOF" + +using namespace std; + + + +/** Number of registers that the TinyRAM has (excluding PC) */ +extern int trNumRegisters; + +/** The bit-length of TinyRAM registers */ +extern int trRegisterLen; + + +/** Numbers of registers including the program counter (used for code clarity) + * Set to trNumRegisters + 1. */ +extern int trNumRegistersInclPC; + +/** Length of an index into the register file, including PC. + * Set to ceil( ::Infrastructure::Log2(trNumRegistersInclPC)). + */ +extern int trRegisterOrPCIndexLen; + +/** Length of an index into the register file, excluding PC. + * Set to ceil( ::Infrastructure::Log2(trNumRegistersPC) ). + */ +extern int trRegisterNoPCIndexLen; + +/** +* Holds the number of possible opcodes +*/ +const int trNumOpcodes = NUMBER_OPCODES; + +/** + * Holds the length of an opcode representation. Should be Log2ceil(trNumOpcodes). + */ +const int trOpcodeLen = ::Infrastructure::Log2ceil(trNumOpcodes); + + +/** + * Set the various RAM parameters. + * Must be called before using any RAM-related variables or classes. + * Must not be called more than once. + */ +void initTinyRAMParams(int numRegisters, int registerLen); +void initTinyRAMParamsFromEnvVariables(); + +/** + * Clears the TinyRAM params + * sets the state as before it was initialized + */ +void clearTinyRAMParams(); + + + + +#endif + diff --git a/tinyram/stark-tinyram/src/TinyRAM/TinyRAMInstance.cpp b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMInstance.cpp new file mode 100644 index 0000000..baa0887 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMInstance.cpp @@ -0,0 +1,161 @@ +#include "TinyRAMInstance.hpp" +#include +#include +#include + + + + using std::string; + + MachineInstruction::MachineInstruction(const Opcode& opcode, const bool arg2isImmediate, + const size_t destIdx, const size_t arg1Idx, const size_t arg2IdxOrImmediate) : + opcode_(opcode), arg2isImmediate_(arg2isImmediate), destIdx_(destIdx), arg1Idx_(arg1Idx), + arg2IdxOrImmediate_(arg2IdxOrImmediate){} + + std::string opcodeToString(const Opcode& op){ + switch(op){ + case Opcode::MEMORY: return "MEMORY"; + case Opcode::NONE: return "NONE"; + case Opcode::AND: return "AND"; + case Opcode::OR: return "OR"; + case Opcode::XOR: return "XOR"; + case Opcode::NOT: return "NOT"; + case Opcode::ADD: return "ADD"; + case Opcode::SUB: return "SUB"; + case Opcode::MULL: return "MULL"; + case Opcode::UMULH: return "UMULH"; + case Opcode::SMULH: return "SMULH"; + case Opcode::UDIV: return "UDIV"; + case Opcode::UMOD: return "UMOD"; + case Opcode::SHL: return "SHL"; + case Opcode::SHR: return "SHR"; + case Opcode::SHAR: return "SHAR"; + case Opcode::CMPE: return "CMPE"; + case Opcode::CMPA: return "CMPA"; + case Opcode::CMPAE: return "CMPAE"; + case Opcode::CMPG: return "CMPG"; + case Opcode::CMPGE: return "CMPGE"; + case Opcode::MOV: return "MOV"; + case Opcode::CMOV: return "CMOV"; + case Opcode::JMP: return "JMP"; + case Opcode::CJMP: return "CJMP"; + case Opcode::CNJMP: return "CNJMP"; + case Opcode::RESERVED_OPCODE_24: return "RESERVED_OPCODE_24"; + case Opcode::RESERVED_OPCODE_25: return "RESERVED_OPCODE_25"; + case Opcode::STOREB: return "STOREB"; + case Opcode::LOADB: return "LOADB"; + case Opcode::STOREW: return "STOREW"; + case Opcode::LOADW: return "LOADW"; + case Opcode::READ: return "READ"; + case Opcode::ANSWER: return "ANSWER"; + case Opcode::NUM_OPCODES: return "NUM_OPCODES"; + default : + std::cout<<"unfamiliar instruction"; + throw("unfamiliar instruction"); + } + } + + Opcode opcodeFromString(const string op){ + if(op == "MEMORY") return Opcode::MEMORY; + if(op == "NONE") return Opcode::NONE; + if(op == "AND") return Opcode::AND; + if(op == "OR") return Opcode::OR; + if(op == "XOR") return Opcode::XOR; + if(op == "NOT") return Opcode::NOT; + if(op == "ADD") return Opcode::ADD; + if(op == "SUB") return Opcode::SUB; + if(op == "MULL") return Opcode::MULL; + if(op == "UMULH") return Opcode::UMULH; + if(op == "SMULH") return Opcode::SMULH; + if(op == "UDIV") return Opcode::UDIV; + if(op == "UMOD") return Opcode::UMOD; + if(op == "SHL") return Opcode::SHL; + if(op == "SHR") return Opcode::SHR; + if(op == "SHAR") return Opcode::SHAR; + if(op == "CMPE") return Opcode::CMPE; + if(op == "CMPA") return Opcode::CMPA; + if(op == "CMPAE") return Opcode::CMPAE; + if(op == "CMPG") return Opcode::CMPG; + if(op == "CMPGE") return Opcode::CMPGE; + if(op == "MOV") return Opcode::MOV; + if(op == "CMOV") return Opcode::CMOV; + if(op == "JMP") return Opcode::JMP; + if(op == "CJMP") return Opcode::CJMP; + if(op == "CNJMP") return Opcode::CNJMP; + if(op == "RESERVED_OPCODE_24") return Opcode::RESERVED_OPCODE_24; + if(op == "RESERVED_OPCODE_25") return Opcode::RESERVED_OPCODE_25; + if(op == "STOREB") return Opcode::STOREB; + if(op == "LOADB") return Opcode::LOADB; + if(op == "STOREW") return Opcode::STOREW; + if(op == "LOADW") return Opcode::LOADW; + if(op == "READ") return Opcode::READ; + if(op == "ANSWER") return Opcode::ANSWER; + if(op == "NUM_OPCODES") return Opcode::NUM_OPCODES; + + std::cout<<"unfamiliar instruction"; + throw("unfamiliar instruction"); + } + + void MachineInstruction::print()const{ + std::cout<1) && (s[0]=='r'); + } + + int getImmidiate(const string s){ + return std::stoul(s); + } + + int getRegNum(const string s){ + if (!isReg(s)){ + std::cout<<"Expected register"< words{it, {}}; + + if(words.size() != 4){ + std::cout<<"Bad format of line, each line must contain exactly 4 words"; + throw("bad format"); + } + + opcode_ = opcodeFromString(words[0]); + destIdx_ = getRegNum(words[1]); + arg1Idx_ = getRegNum(words[2]); + arg2isImmediate_ = !isReg(words[3]); + if(!arg2isImmediate_){ + arg2IdxOrImmediate_ = getRegNum(words[3]); + } + else{ + arg2IdxOrImmediate_ = getImmidiate(words[3]); + } + } + + void TinyRAMProgram::print()const{ + for(const auto& line: code_){ + line.print(); + } + } + + void TinyRAMProgram::addInstructionsFromFile(const std::string filename){ + std::ifstream ifs(filename); + std::string content((std::istreambuf_iterator(ifs)),std::istreambuf_iterator()); + + std::regex regex{R"([\n]+)"}; // split to lines + std::sregex_token_iterator it{content.begin(), content.end(), regex, -1}; + std::vector lines{it, {}}; + + for(const auto& l : lines){ + MachineInstruction instruction(l); + addInstruction(instruction); + } + } diff --git a/tinyram/stark-tinyram/src/TinyRAM/TinyRAMInstance.hpp b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMInstance.hpp new file mode 100644 index 0000000..b4e480b --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAM/TinyRAMInstance.hpp @@ -0,0 +1,149 @@ +/************************************** TinyRAMInstance.hpp **************************************/ +/** + * @file + * + * @brief The file TinyRAMInstance.hpp contains the interface for a TinyRAM instance. + * + * Main classes defined in the file: + * TinyRAMPartialInstance + * TinyRAMFullInstance + */ + /***********************************************************************************************/ + +#ifndef __RAM_INSTANCE_HPP +#define __RAM_INSTANCE_HPP + +#include +#include +#include +#include "TinyRAM/TinyRAMDefinitions.hpp" +#include "common/Utils/ErrorHandling.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using gadgetlib::Opcode; +using gadgetlib::Log2ceil; + + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/**************************** ****************************/ +/**************************** class RAMArchParams ****************************/ +/**************************** ****************************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +struct RAMArchParams { + size_t numRegisters; + size_t registerLength; + + bool operator==(const RAMArchParams& rhs) const; +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/**************************** ****************************/ +/**************************** class TinyRAMProgram ****************************/ +/**************************** ****************************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +/// A data object which holds a TinyRAM code and auxilary information. +struct MachineInstruction { + Opcode opcode_ = Opcode::ANSWER; + bool arg2isImmediate_ = true; + size_t destIdx_ = 0; + size_t arg1Idx_ = 0; + size_t arg2IdxOrImmediate_ = 1; + + MachineInstruction( + const Opcode& opcode, + const bool arg2isImmediate, + const size_t destIdx, + const size_t arg1Idx, + const size_t arg2IdxOrImmediate); + + MachineInstruction(const std::string line); + + void print()const; +}; + +class TinyRAMProgram { +public: + typedef std::vector RAMMachineCode; +private: + std::string name_; + RAMArchParams archParams_; + RAMMachineCode code_; +public: + TinyRAMProgram(const std::string& name, + const RAMArchParams& archParams, + const RAMMachineCode& code) : + name_(name), archParams_(archParams), code_(code) {} + + TinyRAMProgram(const std::string& name, + size_t numRegisters, + size_t wordSize) : + name_(name), archParams_(RAMArchParams{ numRegisters, wordSize }) { + } + + std::string name() const { return name_; } + const RAMMachineCode& code() const { return code_; } + const size_t size() const { return code_.size(); } + const RAMArchParams& archParams() const { return archParams_; } + const MachineInstruction& getInstructionAtPc(const size_t pc) const { return code_[pc]; } + void addInstruction(const MachineInstruction& instruction) { code_.emplace_back(instruction); } + void addInstructionsFromFile(const std::string filename); + unsigned int pcLength() const { + int codeSize = code_.size(); + if (codeSize == 0){ _COMMON_FATAL("TinyRAMProgram : The code is not initialized"); }; + if (codeSize == 1) { return 1; } + return gadgetlib::Log2ceiled(codeSize); + } + + void print()const; + +}; + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/**************************** ****************************/ +/**************************** class TinyRAMProtoboardParams ****************************/ +/**************************** ****************************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class TinyRAMProtoboardParams : public gadgetlib::ProtoboardParams { +private: + RAMArchParams archParams_; + size_t opcodeWidth_; + size_t timeBound_; + size_t pcIncrement_; +public: + TinyRAMProtoboardParams(unsigned int numRegisters, unsigned int registerLength, + size_t opcodeWidth, size_t timeBound, size_t pcIncrement) + : archParams_(RAMArchParams{ numRegisters, registerLength }), + opcodeWidth_(opcodeWidth), + timeBound_(timeBound), pcIncrement_(pcIncrement) {} + TinyRAMProtoboardParams() + : archParams_(RAMArchParams{ 0, 0 }), opcodeWidth_(0), timeBound_(0), pcIncrement_(0) {} + RAMArchParams archParams() const { return archParams_; } + size_t opcodeWidth() const { return opcodeWidth_; } + size_t numRegisters() const { return archParams_.numRegisters; } + size_t registerLength() const { return archParams_.registerLength; } + size_t registerIndexLength() const { return Log2ceil(numRegisters()); } + size_t arg2length() const { return std::max({ registerIndexLength(), registerLength() }); } + size_t numOpcodes() const { return 1u << (opcodeWidth()); } + size_t timeBound() const { return timeBound_; } + size_t pcIncrement() const { return pcIncrement_; } +}; // class TinyRAMProtoboardParams + + +#endif diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/ConstraintSystemToBair/cs2Bair.cpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/ConstraintSystemToBair/cs2Bair.cpp new file mode 100644 index 0000000..b1a2311 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/ConstraintSystemToBair/cs2Bair.cpp @@ -0,0 +1,450 @@ +#include "cs2Bair.hpp" +#include "../RamToContraintSystem/MemoryConsraints.hpp" + +using libstark::BairInstance; +using libstark::BairWitness; +using libstark::ConstraintSys; + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2Bair ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +cs2Bair::cs2Bair(ProtoboardPtr pb,const TinyRAMProgram& program, const int transcriptLen, bool constructWitness) : + pb_(pb), + program_(program), + transcript_len(transcriptLen), + doesProgramUsesMemory_(false), + followingTraceVariable_(program.pcLength(), std::dynamic_pointer_cast(pb_->params())->numRegisters()), + memoryFollowingTraceVariables_(followingTraceVariable_.first_.timeStamp_, followingTraceVariable_.second_.timeStamp_) + { + this->init(); + this->generateConstraints(); + this->boundaryConstraints(); + if(constructWitness){ + generateWitness(); + generateMemoryWitness(); + } +} + +void cs2Bair::init() { + // Init the First Values of timestamp + pb_->addDegreeTranslation(Algebra::FieldElement(getGF2E_X()), 1); + pb_->addDegreeTranslation(Algebra::one(), 0); + // GadgetPtr + transitionFunction_ = TransitionFunction::create(pb_, followingTraceVariable_,memoryFollowingTraceVariables_ , program_); + checkMemoryUse(); + if (doesProgramUsesMemory_){ + memoryConstraints_ = MemoryConstraints::create(pb_, memoryFollowingTraceVariables_); + } +} + +void cs2Bair::boundaryConstraints() const{ + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + pb_->addBoundaryConstraint(followingTraceVariable_.first_.flag_, 0, Algebra::zero()); + pb_->addBoundaryConstraint(followingTraceVariable_.first_.timeStamp_, 0, Algebra::one()); + for (unsigned int i = 0; i < program_.pcLength(); i++){ + pb_->addBoundaryConstraint(followingTraceVariable_.first_.pc_[i], 0, Algebra::zero()); + } + //for (int i = 0; i < params->numRegisters(); i++){ + // pb_->addBoundaryConstraint(followingTraceVariable_.first_.registers_[i], 0, Algebra::zero()); + //} +} + +void cs2Bair::initInitialVars(){ + pb_->val(followingTraceVariable_.first_.flag_) = Algebra::zero(); + pb_->val(followingTraceVariable_.first_.timeStamp_) = Algebra::one(); + int pcLength = program_.pcLength(); + int numRegisters = std::dynamic_pointer_cast(pb_->params())->numRegisters(); + for (int i = 0; i < pcLength; i++){ + pb_->val(followingTraceVariable_.first_.pc_[i]) = Algebra::zero(); + } + for (int i = 0; i < numRegisters; i++){ + pb_->val(followingTraceVariable_.first_.registers_[i]) = Algebra::zero(); + } +} + +void cs2Bair::checkMemoryUse(){ + for (unsigned int i = 0; i < program_.code().size(); ++i){ + Opcode opcode = program_.code()[i].opcode_; + if (opcode == Opcode::STOREB || opcode == Opcode::STOREW || + opcode == Opcode::LOADB || opcode == Opcode::LOADW){ + doesProgramUsesMemory_ = true; + break; + } + } +} + +std::vector cs2Bair::variablesToVector(TraceVariables traceVariables){ + std::vector v; + v.emplace_back(traceVariables.flag_); + v.emplace_back(traceVariables.timeStamp_); + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + for (unsigned int i = 0; i < traceVariables.pc_.size(); i++){ + v.emplace_back(traceVariables.pc_[i]); + } + for (unsigned int i = 0; i < params->numRegisters(); i++){ + v.emplace_back(traceVariables.registers_[i]); + } + return v; +} + +std::vector cs2Bair::memoryVariablesToVector(MemoryTraceVariables traceVariables){ + std::vector v; + v.emplace_back(traceVariables.isMemOp_); + v.emplace_back(traceVariables.isLoad_); + v.emplace_back(traceVariables.timeStamp_); + v.emplace_back(traceVariables.address_); + v.emplace_back(traceVariables.value_); + return v; +} + +void cs2Bair::copyTraceOutputValuesToTraceInput(){ + pb_->val(followingTraceVariable_.first_.timeStamp_) = pb_->val(followingTraceVariable_.second_.timeStamp_); + pb_->val(followingTraceVariable_.first_.flag_) = pb_->val(followingTraceVariable_.second_.flag_); + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + int pcLength = program_.pcLength(); + for (int i = 0; i < pcLength; i++){ + pb_->val(followingTraceVariable_.first_.pc_[i]) = pb_->val(followingTraceVariable_.second_.pc_[i]); + } + for (unsigned int i = 0; i < params->numRegisters(); i++){ + pb_->val(followingTraceVariable_.first_.registers_[i]) = pb_->val(followingTraceVariable_.second_.registers_[i]); + } +} + + +void cs2Bair::createTranslationVector(){ + Algebra::Variable::set traceFirstVariables; + Algebra::Variable::set traceSecondVariables; + + std::vector trace1 = variablesToVector(followingTraceVariable_.first_); + std::vector trace2 = variablesToVector(followingTraceVariable_.second_); + std::vector memoryTrace1 = memoryVariablesToVector(memoryFollowingTraceVariables_.first_); + std::vector memoryTrace2 = memoryVariablesToVector(memoryFollowingTraceVariables_.second_); + + traceFirstVariables.insert(trace1.begin(), trace1.end()); + traceFirstVariables.insert(memoryTrace1.begin(), memoryTrace1.end()); + traceSecondVariables.insert(trace2.begin(), trace2.end()); + traceSecondVariables.insert(memoryTrace2.begin(), memoryTrace2.end()); + + Algebra::Variable::set auxVars = pb_->constraintSystem(Opcode::NONE).getUsedVariables(); + Algebra::Variable::set memoryauxVars = pb_->constraintSystem(Opcode::MEMORY).getUsedVariables(); + auxVars.insert(memoryauxVars.begin(), memoryauxVars.end()); + for (const auto& var : trace1) { + auxVars.erase(var); + + } + for (const auto& var : trace2) { + auxVars.erase(var); + } + for (const auto& var : memoryTrace1) { + auxVars.erase(var); + } + for (const auto& var : memoryTrace2) { + auxVars.erase(var); + } + translation_.insert(translation_.end(), traceFirstVariables.cbegin(), traceFirstVariables.cend()); + translation_.insert(translation_.end(), auxVars.cbegin(), auxVars.cend()); + translation_.insert(translation_.end(), traceSecondVariables.cbegin(), traceSecondVariables.cend()); + Algebra::Variable unUsed("Unused aux Variable"); + for (size_t i = 0; i < auxVars.size(); i++){ + translation_.emplace_back(unUsed); + } + // Jenya - pretty ugly - we leave it like this for now - we'll change it as soon as I have time. + pb_->setNewIndicesForTranslation(translation_); + translation_ = pb_->getTranslationVector(); +} + +void cs2Bair::generateConstraints(){ + transitionFunction_->generateConstraints(); + if (doesProgramUsesMemory_){ + memoryConstraints_->generateConstraints(); + } + createTranslationVector(); +} + +//VariableAssignment <-> vector mappings +std::vector cs2Bair::assignmentToVec(const VariableAssignment& ass)const{ + std::vector res(translation_.size()/2); + for(size_t i=0; i< res.size(); i++){ + res[i] = ass.at(translation_[i]); + } + return res; +} + +VariableAssignment cs2Bair::vectorToAssignment(const std::vector& vec)const{ + VariableAssignment res; + for(size_t i=0; i< translation_.size()/2; i++){ + res[translation_[i]] = vec[i]; + } + return res; +} + +//#define falseWitness//checking how good PCP is at finding small errors +//#define printTrace //print the execution trace (i.e. witness) while generating it +void cs2Bair::generateWitness(){ + //const unsigned int transcript_len = POW2(TRANSCIPT_LEN_LOG) - 1; + // First Assignment should be zero + initInitialVars(); +#ifdef printTrace + set usedVars; +#endif + for (int i = 0; i < transcript_len; ++i){ + ::std::dynamic_pointer_cast(transitionFunction_)->generateWitness(i); + Algebra::VariableAssignment assignment = pb_->assignment(); + traceAssignmentTable_.push_back(assignmentToVec(assignment)); +#ifdef falseWitness + if (i == 3) + traceAssignmentTable_[i][4] = Algebra::xFE(); +#endif + copyTraceOutputValuesToTraceInput(); +#ifdef printTrace + //cout << "var num:" << assignment.size() << endl; + //cout << i << ":" << endl; + for (auto j : assignment){ + if (j.second != Algebra::zero()){ + usedVars.insert(j.first); + // cout << "var num"; + + } + // cout << j.first.name() << ":" << j.second << " "; + } + /* cout << "var num:" << pb_->getUsedVariables().size() << endl; + + for (auto j : pb_->getUsedVariables()){ + cout << j.name() << ":" << pb_->assignment()[j] << endl; + }*/ + //cout << endl; +#endif + } +#ifdef printTrace + + cout << "non-zero-constant vars in trace:" << endl; + for (auto j : usedVars) + cout << j.name() << " " <constraintSystem(Opcode::MEMORY); + } + return ConstraintSystem(); +} + +void cs2Bair::generateMemoryWitness(){ + + Variable::set usedMemoryVariables = pb_->constraintSystem(Opcode::MEMORY).getUsedVariables(); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.first_.timeStamp_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.first_.isMemOp_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.first_.isLoad_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.first_.value_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.first_.address_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.second_.timeStamp_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.second_.isMemOp_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.second_.isLoad_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.second_.value_); + usedMemoryVariables.erase(memoryFollowingTraceVariables_.second_.address_); + + std::vector memoryTrace = pb_->getMemoryTrace(); + std::sort(memoryTrace.begin(), memoryTrace.end(), sortMemoryInfo); + GADGETLIB_ASSERT(memoryTrace.size() == traceAssignmentTable_.size(), "memoryInfo size should be the same as the coloring"); + for (unsigned int i = 0; i < memoryTrace.size(); i++){ + size_t serialNumber1 = memoryTrace[i].getSerialNumber(); + size_t serialNumber2 = memoryTrace[(i + 1) % memoryTrace.size()].getSerialNumber(); + memoryPermutation_[serialNumber1] = serialNumber2; + if (doesProgramUsesMemory_){ + VariableAssignment currAssignment = vectorToAssignment(traceAssignmentTable_[serialNumber1]); + VariableAssignment nextAssignment = vectorToAssignment(traceAssignmentTable_[serialNumber2]); + pb_->clearAssignment(); + pb_->val(memoryFollowingTraceVariables_.first_.timeStamp_) = currAssignment[memoryFollowingTraceVariables_.first_.timeStamp_]; + pb_->val(memoryFollowingTraceVariables_.first_.isMemOp_) = currAssignment[memoryFollowingTraceVariables_.first_.isMemOp_]; + pb_->val(memoryFollowingTraceVariables_.first_.isLoad_) = currAssignment[memoryFollowingTraceVariables_.first_.isLoad_]; + pb_->val(memoryFollowingTraceVariables_.first_.value_) = currAssignment[memoryFollowingTraceVariables_.first_.value_]; + pb_->val(memoryFollowingTraceVariables_.first_.address_) = memoryTrace[i].getAddress(); + + pb_->val(memoryFollowingTraceVariables_.second_.timeStamp_) = nextAssignment[memoryFollowingTraceVariables_.first_.timeStamp_]; + pb_->val(memoryFollowingTraceVariables_.second_.isMemOp_) = nextAssignment[memoryFollowingTraceVariables_.first_.isMemOp_]; + pb_->val(memoryFollowingTraceVariables_.second_.isLoad_) = nextAssignment[memoryFollowingTraceVariables_.first_.isLoad_]; + pb_->val(memoryFollowingTraceVariables_.second_.value_) = nextAssignment[memoryFollowingTraceVariables_.first_.value_]; + pb_->val(memoryFollowingTraceVariables_.second_.address_) = memoryTrace[(i+1) % memoryTrace.size()].getAddress(); + if (Algebra::one() == pb_->val(memoryFollowingTraceVariables_.first_.isMemOp_)){ + + memoryConstraints_->generateWitness(); + + if (i+1 != memoryTrace.size()){ + GADGETLIB_ASSERT(pb_->isSatisfied(Opcode::MEMORY), "MemoryConstraints are not satisfied"); + for (const Variable& v : usedMemoryVariables){ + currAssignment[v] = pb_->val(v); + } + } + else{ + for (const Variable& v : usedMemoryVariables){ + currAssignment[v] = Algebra::zero(); + } + } + traceAssignmentTable_[serialNumber1] = assignmentToVec(currAssignment); + } + } + } +} + + +size_t cs2Bair::varsAmount() const{ + std::vector translation = pb_->getTranslationVector(); + GADGETLIB_ASSERT(translation.size() % 2 == 0, "translation vector size is expected to be even."); + /* + return translation_.size() / 2; + */ + // Michael said that he wants the size of all the variables. + return translation.size(); +} + + +BairInstance::boundaryConstraints_t cs2Bair::getBoundaryConstraints() const{ + BairInstance::boundaryConstraints_t boundaryConstraints; + + BoundaryVariables boundaryVariables = pb_->boundaryVariables(); + BoundaryTimestamps boundaryTimestamps = pb_->boundaryTimestamps(); + BoundaryAssignments boundaryAssignment = pb_->boundaryAssignments(); + + _COMMON_ASSERT(boundaryVariables.size() == boundaryTimestamps.size(), + "Number of variables should be equal to the number of timestamps in boundary constaints"); + _COMMON_ASSERT(boundaryAssignment.size() == boundaryVariables.size(), + "Number of variabled should be equal to the number os assignments in boundary constraints"); + + for (unsigned int i = 0; i < boundaryVariables.size(); ++i){ + for (unsigned int j = 0; j < translation_.size(); ++j){ + if (translation_[j] == boundaryVariables[i]){ + BairInstance::point_t location(boundaryTimestamps[i], j); + boundaryConstraints[location] = boundaryAssignment[i]; + } + } + } + return boundaryConstraints; +} + +Algebra::Variable::set cs2Bair::getStateVariables() const { + Algebra::Variable::set retSet; + retSet.insert(followingTraceVariable_.first_.flag_); + retSet.insert(followingTraceVariable_.second_.flag_); + retSet.insert(followingTraceVariable_.first_.timeStamp_); + retSet.insert(followingTraceVariable_.second_.timeStamp_); + GADGETLIB_ASSERT(followingTraceVariable_.first_.pc_.size() == followingTraceVariable_.second_.pc_.size(), + "CS2Bair: unpacked pc should have the exact same size in both of the states"); + for (unsigned int i = 0; i < followingTraceVariable_.first_.pc_.size(); i++) { + retSet.insert(followingTraceVariable_.first_.pc_[i]); + retSet.insert(followingTraceVariable_.second_.pc_[i]); + } + GADGETLIB_ASSERT(followingTraceVariable_.first_.registers_.size() == followingTraceVariable_.second_.registers_.size(), + "CS2Bair: number of registers should be the same in both of the states"); + for (unsigned int i = 0; i < followingTraceVariable_.first_.registers_.size(); i++) { + retSet.insert(followingTraceVariable_.first_.registers_[i]); + retSet.insert(followingTraceVariable_.second_.registers_[i]); + } + return retSet; +} + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairConstraints ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +cs2BairConstraints::cs2BairConstraints(const cs2Bair& cs2bair) : numVars_(cs2bair.varsAmount()){ + ConstraintSystem cs = cs2bair.getConstraints(); + for(const auto& p: cs.getConstraintPolynomials()){ + constraints_.push_back(p->clone()); + } +}; + +cs2BairConstraints::cs2BairConstraints(const polySet_t& constraints, const size_t numVars):numVars_(numVars){ + for(const auto& p: constraints){ + constraints_.push_back(p->clone()); + } +} + +size_t cs2BairConstraints::numVars() const{ + return numVars_;; +} +const ConstraintSys::polySet_t& cs2BairConstraints::constraints() const{ + return constraints_; +} + +cs2BairConstraints* cs2BairConstraints::clone() const{ + return new cs2BairConstraints(constraints_, numVars_); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairColoring ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +cs2BairColoring::cs2BairColoring(const cs2Bair& cs2bair) : traceAssignments_(cs2bair.getTraceAssignmet()){ + translation_ = cs2bair.getTranslationVector(); +}; + +BairWitness::color_t cs2BairColoring::getElementByIndex(index_t index) const{ + GADGETLIB_ASSERT(index < traceAssignments_.size(), "Attempted to get an illegal element; " +std::to_string(index) + " >= " + std::to_string(traceAssignments_.size())); + return traceAssignments_[index]; +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairMemory ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +cs2BairMemory::cs2BairMemory(const cs2Bair& cs2bair) : memoryPermutation_(cs2bair.getMemoryPermutation()){} + +size_t cs2BairMemory::getElementByIndex(index_t index) const{ + return memoryPermutation_.at(index); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairCS ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +cs2BairMemoryCS::cs2BairMemoryCS(const cs2Bair& cs2bair) : numVars_(cs2bair.varsAmount()){ + ConstraintSystem cs = cs2bair.getMemoryConstraints(); + constraints_ = cs.getConstraintPolynomials(); +}; + +cs2BairMemoryCS::cs2BairMemoryCS(const polySet_t& constraints, const size_t numVars):numVars_(numVars){ + for(const auto& p: constraints){ + constraints_.push_back(p->clone()); + } +} + +size_t cs2BairMemoryCS::numVars() const{ + return numVars_; +} + +const ConstraintSys::polySet_t& cs2BairMemoryCS::constraints() const { + return constraints_; +} + +cs2BairMemoryCS* cs2BairMemoryCS::clone() const{ + return new cs2BairMemoryCS(constraints_,numVars_); +} + + + diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/ConstraintSystemToBair/cs2Bair.hpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/ConstraintSystemToBair/cs2Bair.hpp new file mode 100644 index 0000000..43adce5 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/ConstraintSystemToBair/cs2Bair.hpp @@ -0,0 +1,159 @@ +#ifndef _COMMON_CONSTRAINTSLIB2_CONSTRAINTSYSTEMTOBair_cs2Bair_HPP_ +#define _COMMON_CONSTRAINTSLIB2_CONSTRAINTSYSTEMTOBair_cs2Bair_HPP_ + +#include "gadgetlib/common_use.hpp" +#include "TinyRAMtoBair/RamToContraintSystem/transitionFunction.hpp" +#include "TinyRAMtoBair/RamToContraintSystem/transitionFunction.hpp" +#include +#include +#include + + + + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2Bair ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class cs2Bair{ +private: + // Input Variables + ProtoboardPtr pb_; + TinyRAMProgram program_; + + // Internal Variables + int transcript_len; + // Is there any Memory Use in the program + bool doesProgramUsesMemory_; + // Memory and Regular constraints + GadgetPtr transitionFunction_; + GadgetPtr memoryConstraints_; + // Trace Variables for regular and memory constraints. + FollowingTraceVariables followingTraceVariable_; + MemoryFollowingTraceVariables memoryFollowingTraceVariables_; + // Translation vector between CS variables to Bair + std::vector translation_; + + std::vector> traceAssignmentTable_; + std::map memoryPermutation_; + + // Help Functions + void initInitialVars(); + void checkMemoryUse(); + void copyTraceOutputValuesToTraceInput(); + void createTranslationVector(); + std::vector variablesToVector(TraceVariables traceVariables); + std::vector memoryVariablesToVector(MemoryTraceVariables traceVariables); + void generateMemoryConstraints() const; + void boundaryConstraints() const; + Algebra::Variable::set getStateVariables() const; + + //Functions + void init(); + void generateConstraints(); + void generateWitness(); + void generateMemoryWitness(); + + //VariableAssignment <-> vector mappings + std::vector assignmentToVec(const VariableAssignment& ass)const; + VariableAssignment vectorToAssignment(const std::vector& vec)const; + +public: + cs2Bair(ProtoboardPtr pb, const TinyRAMProgram& program, int transcriptLen, bool constructWitness); + + ConstraintSystem getConstraints() const { return pb_->constraintSystem(Opcode::NONE); } + ConstraintSystem getMemoryConstraints() const; + + const std::vector>& getTraceAssignmet()const { return traceAssignmentTable_; } + std::vector getTranslationVector() const { return pb_->getTranslationVector(); } + const std::map& getMemoryPermutation()const { return memoryPermutation_; } + size_t varsAmount() const; // Number of Variables in one traceLine + libstark::BairInstance::boundaryConstraints_t getBoundaryConstraints() const; +}; // cs2Bair + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairConstraints ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class cs2BairConstraints :public libstark::ConstraintSys { +private: + polySet_t constraints_; + const size_t numVars_; +public: + cs2BairConstraints(const cs2Bair& cs2bair); + cs2BairConstraints(const polySet_t& constraints, const size_t numVars); + size_t numVars() const; // amount of variables in 1 traceline (including aux variables) + const polySet_t& constraints() const; + cs2BairConstraints* clone() const; +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairColoring ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class cs2BairColoring : public libstark::BairWitness::assignment_t{ +private: + const std::vector> traceAssignments_; + std::vector translation_; +public: + cs2BairColoring(const cs2Bair& cs2bair); + libstark::BairWitness::color_t getElementByIndex(index_t index) const; +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairMemory ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +// For now - stub. We don't use memory permutation +class cs2BairMemory : public libstark::Sequence{ +private: + const std::map memoryPermutation_; +public: + cs2BairMemory(const cs2Bair& cs2bair); + size_t getElementByIndex(index_t index) const; + +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* cs2BairCS ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +//ForNow we use empty constraint system +class cs2BairMemoryCS : public libstark::ConstraintSys { +private: + polySet_t constraints_; + const size_t numVars_; + +public: + cs2BairMemoryCS(const cs2Bair& cs2bair); + cs2BairMemoryCS(const polySet_t& constraints, const size_t numVars); + size_t numVars() const; + const polySet_t& constraints() const; + cs2BairMemoryCS* clone()const; +}; + + + +#endif // _COMMON_CONSTRAINTSLIB2_CONSTRAINTSYSTEMTOBair_cs2Bair_HPP_ diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALU.cpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALU.cpp new file mode 100644 index 0000000..e98a196 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALU.cpp @@ -0,0 +1,1883 @@ +#include +#include +#include +#include "ALU.hpp" +#include +#include + +//#include +#define EXTDIM Algebra::ExtensionDegree +using namespace std; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* General Variables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +Algebra::Variable pInverse_("pInverse"); +UnpackedWord unpackedArg1_(REGISTER_LENGTH, "unpackedArg1_"); +UnpackedWord unpackedArg2_(REGISTER_LENGTH, "unpackedArg2_"); +UnpackedWord opcodeResult_(REGISTER_LENGTH, "opcodeResult_"); +UnpackedWord opcodeCarry_(REGISTER_LENGTH, "opcodeCarry_"); +UnpackedWord & opcodeAux1_ = opcodeResult_; +UnpackedWord & opcodeAux2_ = opcodeCarry_; +UnpackedWord opcodeAux3_(REGISTER_LENGTH, "opcodeAux3_"); +UnpackedWord opcodeAux4_(REGISTER_LENGTH, "opcodeAux4_"); +UnpackedWord opcodeAux5_(REGISTER_LENGTH, "opcodeAux5_"); +UnpackedWord opcodeAux6_(REGISTER_LENGTH, "opcodeAux6_"); +UnpackedWord opcodeAux7_(REGISTER_LENGTH, "opcodeAux7_"); + +//lame, move all to TinyRAMProtoboardParams? +unsigned int prngseed; +bool standAlone_ = false; +FElem program_output = Algebra::one(); //use any incorrect != -1 value to test soundness +int max_timestep = 1; +unsigned int ROMSIZE = 0; + +void resetALU_GadgetGlobalState(){ + max_timestep = 1; +} + +void initMemResult(ProtoboardPtr pb, const ALUOutput& res){ + pb->val(res.isMemOp_) = Algebra::zero(); + pb->val(res.isLoadOp_) = Algebra::zero(); + pb->val(res.value_) = Algebra::zero(); + pb->val(res.address_) = Algebra::zero(); + +} + +void initGeneralOpcodes(ProtoboardPtr pb){ + pb->val(pInverse_) = Algebra::zero(); + for (int i = 0; i < REGISTER_LENGTH; i++){ + // Other opcodes use them and we multiply the constraint by selector + // In order to be evaluated correctly the must be initialized as 0 + pb->val(unpackedArg1_[i]) = Algebra::zero(); + pb->val(unpackedArg2_[i]) = Algebra::zero(); + pb->val(opcodeResult_[i]) = Algebra::zero(); + pb->val(opcodeCarry_[i]) = Algebra::zero(); + pb->val(opcodeAux3_[i]) = Algebra::zero(); + pb->val(opcodeAux4_[i]) = Algebra::zero(); + pb->val(opcodeAux5_[i]) = Algebra::zero(); + pb->val(opcodeAux6_[i]) = Algebra::zero(); + pb->val(opcodeAux7_[i]) = Algebra::zero(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_Component_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +const ALU_Component_Gadget::TRParamsPtr ALU_Component_Gadget::tinyRAMparams() const { + return std::dynamic_pointer_cast(pb_->params()); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +GadgetPtr ALU_Gadget::create(ProtoboardPtr pb, + const ALUInput& inputVariables, + const ALUOutput& resultVariables) { + GadgetPtr pGadget(new ALU_Gadget(pb, inputVariables, resultVariables)); + pGadget->init(); + return pGadget; +} + +ALU_Gadget::ALU_Gadget(ProtoboardPtr pb, + const ALUInput& inputVariables, + const ALUOutput& resultVariables) + : + Gadget(pb), + program_("program", + std::dynamic_pointer_cast(pb_->params())->numRegisters(), + std::dynamic_pointer_cast(pb_->params())->registerLength()), + inputVariables_(inputVariables), + resultVariables_(resultVariables){} + +void ALU_Gadget::init() { + createInternalComponents(); + //this used to appear in internal inits. Moved it here so won't be repetition of booleanity checks multiplied by selectors + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputVariables_.arg1_val_, PackingMode::UNPACK, Opcode::NONE); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputVariables_.arg2_val_, PackingMode::UNPACK, Opcode::NONE); + +} + +void ALU_Gadget::createInternalComponents() { + components_[Opcode::XOR] = ALU_XOR_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::AND] = ALU_AND_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::OR] = ALU_OR_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::NOT] = ALU_NOT_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::ADD] = ALU_ADD_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::SUB] = ALU_SUB_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::MULL] = ALU_MULL_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::UMULH] = ALU_UMULH_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::SMULH] = ALU_SMULH_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::UDIV] = ALU_UDIV_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::UMOD] = ALU_UDIV_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::CMPE] = ALU_CMPE_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::CMPA] = ALU_CMPA_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::CMPAE] = ALU_CMPAE_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::CMPG] = ALU_CMPG_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::CMPGE] = ALU_CMPGE_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::SHL] = ALU_SHL_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::SHR] = ALU_SHR_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::LOADW] = ALU_LOADW_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::STOREW] = ALU_STOREW_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::JMP] = ALU_JMP_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::ANSWER] = ALU_ANSWER_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::CJMP] = ALU_CJMP_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::CNJMP] = ALU_CNJMP_Gadget::create(pb_, inputVariables_, + resultVariables_); + components_[Opcode::RESERVED_OPCODE_24] = + ALU_RESERVED_OPCODE_24_Gadget::create(pb_, inputVariables_,resultVariables_); + components_[Opcode::MOV] = ALU_MOV_Gadget::create(pb_, inputVariables_, + resultVariables_); +} + +void ALU_Gadget::setProgram(const TinyRAMProgram& program){ + program_ = program; +} + + + + +set getUsedOpcodes(const TinyRAMProgram& program){ + set retVal; + for (auto inst : program.code()) { + retVal.insert(inst.opcode_); + } + return retVal; +} + +map getRelevantConstraints(const TinyRAMProgram& program, const size_t& constraintIndex, const ProtoboardPtr& pb){ + map retVal; + set opcodes = getUsedOpcodes(program); + for (auto opcode : opcodes) { + if (pb->constraintSystem(opcode).getNumberOfConstraints() > constraintIndex) { + retVal[opcode] = pb->constraintSystem(opcode).getConstraints()[constraintIndex].constraint(); + } + } + return retVal; +} + +size_t getMaxConstraintNum(const TinyRAMProgram& program,const ProtoboardPtr& pb){ + set opcodes = getUsedOpcodes(program); + size_t retVal = 0; + for (const auto& opcode : opcodes) { + retVal = max(retVal, pb->constraintSystem(opcode).getNumberOfConstraints()); + } + return retVal; +} + +void ALU_Gadget::generateConstraints() { + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + //standAlone_ = false; //uncomment if needed + for (auto& componentPair : components_) { + componentPair.second->generateConstraints(); + } + GADGETLIB_ASSERT(program_.size() > 0, "The program should be initalized"); + + unsigned int programLength = (unsigned int)(program_.size()); + size_t maxConstraintNum = getMaxConstraintNum(program_, pb_); + for (unsigned int i = 0; i < maxConstraintNum; i++) { + map opToConstraints = getRelevantConstraints(program_, i, pb_); + vector constraints; + map M; + for (const auto& a : opToConstraints){ + constraints.push_back(a.second); + M[a.first] = constraints.size() - 1; + } + vector opcodeVars = getPCVars(inputVariables_.pc_); + + vector selectorToConstraint(programLength); + vector selectorRelevant(programLength); + for (unsigned int j = 0; j < programLength; j++) { + Opcode currentOpcode = program_.code()[j].opcode_; + if (opToConstraints.find(currentOpcode) != opToConstraints.end()){ + selectorRelevant[j] = true; + selectorToConstraint[j] = M[currentOpcode]; + } + else { + selectorRelevant[j] = false; + } + } + CircuitPolynomial S(SelectorSum(constraints, opcodeVars, selectorToConstraint, selectorRelevant)); + pb_->addGeneralConstraint(S, "SelectorSum_" + i, Opcode::NONE); + } +} + +void ALU_Gadget::generateWitness(unsigned int i) { + GADGETLIB_ASSERT(i < program_.size(), "The value should be less than the program length"); + Opcode opcode = program_.code()[i].opcode_; + switch (opcode) + { + case gadgetlib::Opcode::NONE: + GADGETLIB_FATAL("The opcode can't be NONE in the program"); + break; + case gadgetlib::Opcode::AND: + components_[Opcode::AND]->generateWitness(); + break; + case gadgetlib::Opcode::OR: + components_[Opcode::OR]->generateWitness(); + break; + case gadgetlib::Opcode::XOR: + components_[Opcode::XOR]->generateWitness(); + break; + case gadgetlib::Opcode::NOT: + components_[Opcode::NOT]->generateWitness(); + break; + case gadgetlib::Opcode::ADD: + components_[Opcode::ADD]->generateWitness(); + break; + case gadgetlib::Opcode::SUB: + components_[Opcode::SUB]->generateWitness(); + break; + case gadgetlib::Opcode::MULL: + components_[Opcode::MULL]->generateWitness(); + break; + case gadgetlib::Opcode::UMULH: + components_[Opcode::UMULH]->generateWitness(); + break; + case gadgetlib::Opcode::SMULH: + components_[Opcode::SMULH]->generateWitness(); + break; + case gadgetlib::Opcode::UDIV: + components_[Opcode::UDIV]->generateWitness(); + break; + case gadgetlib::Opcode::UMOD: + components_[Opcode::UMOD]->generateWitness(); + break; + case gadgetlib::Opcode::SHL: + components_[Opcode::SHL]->generateWitness(); + break; + case gadgetlib::Opcode::SHR: + components_[Opcode::SHR]->generateWitness(); + break; + case gadgetlib::Opcode::SHAR: + break; + case gadgetlib::Opcode::CMPE: + components_[Opcode::CMPE]->generateWitness(); + break; + case gadgetlib::Opcode::CMPA: + components_[Opcode::CMPA]->generateWitness(); + break; + case gadgetlib::Opcode::CMPAE: + components_[Opcode::CMPAE]->generateWitness(); + break; + case gadgetlib::Opcode::CMPG: + components_[Opcode::CMPG]->generateWitness(); + break; + case gadgetlib::Opcode::CMPGE: + components_[Opcode::CMPGE]->generateWitness(); + break; + case gadgetlib::Opcode::MOV: + components_[Opcode::MOV]->generateWitness(); + break; + case gadgetlib::Opcode::CMOV: + break; + case gadgetlib::Opcode::JMP: + components_[Opcode::JMP]->generateWitness(); + break; + case gadgetlib::Opcode::CJMP: + components_[Opcode::CJMP]->generateWitness(); + break; + case gadgetlib::Opcode::CNJMP: + components_[Opcode::CNJMP]->generateWitness(); + break; + case gadgetlib::Opcode::RESERVED_OPCODE_24: + components_[Opcode::RESERVED_OPCODE_24]->generateWitness(); + break; + case gadgetlib::Opcode::RESERVED_OPCODE_25: + break; + case gadgetlib::Opcode::STOREB: + break; + case gadgetlib::Opcode::LOADB: + break; + case gadgetlib::Opcode::STOREW: + components_[Opcode::STOREW]->generateWitness(); + break; + case gadgetlib::Opcode::LOADW: + components_[Opcode::LOADW]->generateWitness(); + break; + case gadgetlib::Opcode::READ: + break; + case gadgetlib::Opcode::ANSWER: + components_[Opcode::ANSWER]->generateWitness(); + return; //break; + case gadgetlib::Opcode::NUM_OPCODES: + break; + default: + break; + } + ++max_timestep; +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_XOR_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +ALU_XOR_Gadget::ALU_XOR_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +void ALU_XOR_Gadget::init(){} + +GadgetPtr ALU_XOR_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_XOR_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_XOR_Gadget::generateConstraints(){ + CircuitPolynomial poly(inputs_.arg1_val_ + inputs_.arg2_val_ + results_.result_); + pb_->addGeneralConstraint(poly,"arg1_val + arg2_val + dest_val = 0",Opcode::XOR); + // Flag constraint + CircuitPolynomial flagConstarint1(results_.result_ * results_.flag_); + CircuitPolynomial flagConstarint2(results_.result_ * pInverse_); + CircuitPolynomial flagConstraint3(Algebra::one() + results_.flag_); + pb_->addGeneralConstraint(flagConstarint1, "flag * result= 0", Opcode::XOR); + pb_->addGeneralConstraint(flagConstarint2 + flagConstraint3, "result * invResult = 1 - flag", Opcode::XOR); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::XOR); +} + +void ALU_XOR_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + Algebra::FElem res = pb_->val(inputs_.arg1_val_) + pb_->val(inputs_.arg2_val_); + pb_->val(results_.result_) = res; + pb_->val(results_.flag_) = (res == Algebra::zero()) ? Algebra::one() : Algebra::zero(); + pb_->val(pInverse_) = (res != Algebra::zero()) ? res.inverse() : Algebra::one(); + GADGETLIB_ASSERT(unpackedArg1_.size() == unpackedArg2_.size(), "XOR_GADGET: Unpacked1.size() == Unpacked2.size()"); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_AND_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +ALU_AND_Gadget::ALU_AND_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) +:Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +void ALU_AND_Gadget::init(){ + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::AND); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::AND); +} + +GadgetPtr ALU_AND_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_AND_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_AND_Gadget::generateConstraints(){ + if (standAlone_){ + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + } + size_t registerLength = tinyRAMparams()->registerLength(); + CircuitPolynomial polynomial; + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + for (unsigned int i = 0; i < registerLength; ++i){ + polynomial = polynomial + (unpackedArg1_[i] * unpackedArg2_[i] * x_i); + x_i *= x; + } + pb_->addGeneralConstraint(polynomial + results_.result_, "sum_{i=0}^{registerLength-1} uinput1[i] * uinput2[i] * X^i + result = 0", Opcode::AND); + // Flag constraint + CircuitPolynomial flagConstraint1({ results_.result_, results_.flag_ });//{a,b} means the product of A and B - and in this notation uses the faster (prod of) LinearCombination eval function + CircuitPolynomial flagConstraint2({ results_.result_, pInverse_ }); + CircuitPolynomial flagConstraint3(Algebra::one() + results_.flag_); + pb_->addGeneralConstraint(flagConstraint1, "flag * result= 0", Opcode::AND); + pb_->addGeneralConstraint(flagConstraint2 + flagConstraint3, "result * invResult = 1 - flag", Opcode::AND); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::AND); +} + +void ALU_AND_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + size_t registerLength = tinyRAMparams()->registerLength(); + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + Algebra::FElem res = Algebra::zero(); + for (unsigned int i = 0; i < registerLength; i++){ + res += pb_->val(unpackedArg1_[i]) * pb_->val(unpackedArg2_[i]) * x_i; + x_i *= x; + } + pb_->val(results_.result_) = res; + pb_->val(results_.flag_) = (res == Algebra::zero()) ? Algebra::one() : Algebra::zero(); + pb_->val(pInverse_) = (res != Algebra::zero()) ? res.inverse() : Algebra::one(); + for (int i = 0; i < REGISTER_LENGTH; i++){ + // Other opcodes use them and we multiply the constraint by selector + // In order to be evaluated correctly the must be initialized as 0 + pb_->val(opcodeResult_[i]) = Algebra::zero(); + pb_->val(opcodeCarry_[i]) = Algebra::zero(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_OR_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +ALU_OR_Gadget::ALU_OR_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) +:Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + + +void ALU_OR_Gadget::init(){ + //size_t registerLength = tinyRAMparams()->registerLength(); + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::OR); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::OR); + +} + +GadgetPtr ALU_OR_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_OR_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_OR_Gadget::generateConstraints(){ + if (standAlone_){ + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + } + size_t registerLength = tinyRAMparams()->registerLength(); + CircuitPolynomial polynomial; + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + for (unsigned int i = 0; i < registerLength; ++i){ + LinearCombination first = Algebra::one() + unpackedArg1_[i]; + LinearCombination second = Algebra::one() + unpackedArg2_[i]; + CircuitPolynomial tmpPoly(first * second); + polynomial = polynomial + (tmpPoly + Algebra::one()) * x_i; + x_i *= x; + } + pb_->addGeneralConstraint(polynomial + results_.result_, "sum_{i=0}^{registerLength-1} (1+(1+uinput1[i]) * (1+uinput2[i])) * X^i + result = 0", Opcode::OR); + // Flag constraint + CircuitPolynomial flagConstarint1(results_.result_ * results_.flag_); + CircuitPolynomial flagConstarint2(results_.result_ * pInverse_); + CircuitPolynomial flagConstraint3(Algebra::one() + results_.flag_); + pb_->addGeneralConstraint(flagConstarint1, "flag * result= 0", Opcode::OR); + pb_->addGeneralConstraint(flagConstarint2 + flagConstraint3, "result * invResult = 1 - flag", Opcode::OR); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::OR); +} + +void ALU_OR_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + size_t registerLength = tinyRAMparams()->registerLength(); + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + Algebra::FElem res=Algebra::zero(); + for (unsigned int i = 0; i < registerLength; i++){ + Algebra::FElem first = Algebra::one() + pb_->val(unpackedArg1_[i]); + Algebra::FElem second = Algebra::one() + pb_->val(unpackedArg2_[i]); + res += (Algebra::one() + first*second) * x_i; + x_i *= x; + } + pb_->val(results_.result_) = res; + pb_->val(results_.flag_) = (res == Algebra::zero()) ? Algebra::one() : Algebra::zero(); + pb_->val(pInverse_) = (res != Algebra::zero()) ? res.inverse() : Algebra::one(); + for (int i = 0; i < REGISTER_LENGTH; i++){ + // Other opcodes use them and we multiply the constraint by selector + // In order to be evaluated correctly the must be initialized as 0 + pb_->val(opcodeResult_[i]) = Algebra::zero(); + pb_->val(opcodeCarry_[i]) = Algebra::zero(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_NOT_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +ALU_NOT_Gadget::ALU_NOT_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) +:Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + + +void ALU_NOT_Gadget::init(){} + +GadgetPtr ALU_NOT_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_NOT_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_NOT_Gadget::generateConstraints(){ + size_t registerLength = tinyRAMparams()->registerLength(); + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + Algebra::FElem allOnes = Algebra::zero(); + for (unsigned int i = 0; i < registerLength; i++){ + allOnes += x_i; + x_i *= x; + } + CircuitPolynomial poly(allOnes + inputs_.arg1_val_ + results_.result_); + pb_->addGeneralConstraint(poly, "(111...1) + arg1 + result = 0", Opcode::NOT); + // Flag constraint + CircuitPolynomial flagConstraint1(results_.result_ * results_.flag_); + CircuitPolynomial flagConstraint2(results_.result_ * pInverse_); + CircuitPolynomial flagConstraint3(Algebra::one() + results_.flag_); + pb_->addGeneralConstraint(flagConstraint1, "flag * result= 0", Opcode::NOT); + pb_->addGeneralConstraint(flagConstraint2 + flagConstraint3, "result * invResult = 1 - flag", Opcode::NOT); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::NOT); +} + +void ALU_NOT_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + size_t registerLength = tinyRAMparams()->registerLength(); + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::FElem x_i = Algebra::one(); // will hold x^i + Algebra::FElem allOnes = Algebra::zero(); + for (unsigned int i = 0; i < registerLength; i++){ + allOnes += x_i; + x_i *= x; + } + Algebra::FElem res = allOnes + pb_->val(inputs_.arg1_val_); + pb_->val(results_.result_) = res ; + pb_->val(results_.flag_) = (res == Algebra::zero()) ? Algebra::one() : Algebra::zero(); + pb_->val(pInverse_) = (res != Algebra::zero()) ? res.inverse() : Algebra::one(); + for (int i = 0; i < REGISTER_LENGTH; i++){ + // Other opcodes use them and we multiply the constraint by selector + // In order to be evaluated correctly the must be initialized as 0 + pb_->val(unpackedArg1_[i]) = Algebra::zero(); + pb_->val(unpackedArg2_[i]) = Algebra::zero(); + pb_->val(opcodeResult_[i]) = Algebra::zero(); + pb_->val(opcodeCarry_[i]) = Algebra::zero(); + } +} + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_ADD_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_ADD_Gadget::ALU_ADD_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_ADD_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_ADD_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_ADD_Gadget::init(){ + //size_t registerLength = tinyRAMparams()->registerLength(); + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::ADD); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::ADD); + packResult_g_ = CompressionPacking_Gadget::create(pb_, opcodeResult_, results_.result_, PackingMode::PACK, Opcode::ADD); + add_g_ = Addition_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, opcodeResult_, opcodeCarry_, results_.flag_, Opcode::ADD); +} + +void ALU_ADD_Gadget::generateConstraints(){ + //size_t registerLength = tinyRAMparams()->registerLength(); + if (standAlone_){ + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + } + add_g_->generateConstraints(); + packResult_g_->generateConstraints(); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::SUB); +} +void ALU_ADD_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + add_g_->generateWitness(); + packResult_g_->generateWitness(); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SUB_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_SUB_Gadget::ALU_SUB_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_SUB_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_SUB_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_SUB_Gadget::init(){ + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::SUB); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, results_.result_, PackingMode::UNPACK, Opcode::SUB); + packResult_g_ = CompressionPacking_Gadget::create(pb_, opcodeResult_, inputs_.arg1_val_, PackingMode::PACK, Opcode::SUB); + add_g_ = Addition_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, opcodeResult_, opcodeCarry_, results_.flag_, Opcode::SUB); +} + +void ALU_SUB_Gadget::generateConstraints(){ + if (standAlone_){ + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + } + add_g_->generateConstraints(); + packResult_g_->generateConstraints(); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::SUB); +} +void ALU_SUB_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpackArg1_g_->generateWitness(); + size_t registerLength = tinyRAMparams()->registerLength(); + size_t operand1 = mapFieldElementToInteger(0, registerLength, val(inputs_.arg1_val_)); + size_t operand2 = mapFieldElementToInteger(0, registerLength, val(inputs_.arg2_val_)); + val(results_.result_) = mapIntegerToFieldElement(0, registerLength, /* 0x100000000ULL + */ operand1 - operand2); + unpackArg2_g_->generateWitness(); + add_g_->generateWitness(); + //don't do: packResult_g_->generateWitness(); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_MULL_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_MULL_Gadget::ALU_MULL_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), + multPartials1_(opcodeAux1_), multPartials2_(opcodeAux2_), unpackedResult_(opcodeAux3_), + witnessHighBits_(opcodeAux4_), dmultPartials1_(opcodeAux5_), dmultPartials2_(opcodeAux6_){} + +GadgetPtr ALU_MULL_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_MULL_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_MULL_Gadget::init(){ + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::MULL); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::MULL); + mult_g_ = Multiplication_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, multPartials1_, multPartials2_, false, Opcode::MULL); + unpackResult_g_ = CompressionPacking_Gadget::create(pb_, unpackedResult_, results_.result_, PackingMode::UNPACK, Opcode::MULL); + dmultPack_g_ = DoubleMultPack_Gadget::create(pb_, unpackedResult_, witnessHighBits_, dmultPartials1_, dmultPartials2_, multPartials2_[0], false, Opcode::MULL); + packHighBits_g_ = CompressionPacking_Gadget::create(pb_, witnessHighBits_, multPartials1_[0], PackingMode::PACK, Opcode::MULL); +} + +void ALU_MULL_Gadget::generateConstraints(){ + if (standAlone_){ + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + } + mult_g_->generateConstraints(); + unpackResult_g_->generateConstraints(); + const size_t & registerLength = tinyRAMparams()->registerLength(); + for (unsigned int i = 0; i < registerLength; ++i) { + enforceBooleanity(witnessHighBits_[i], Opcode::MULL); + } + dmultPack_g_->generateConstraints(); + //flag: + packHighBits_g_->generateConstraints(); + pb_->addGeneralConstraint(multPartials1_[0] * (multPartials1_[0] * pInverse_ + Algebra::one()), "flag inv", Opcode::MULL); + pb_->addGeneralConstraint(results_.flag_ + multPartials1_[0] * pInverse_, "mull flag", Opcode::MULL); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::MULL); +} + +void ALU_MULL_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + const size_t & registerLength = tinyRAMparams()->registerLength(); + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + mult_g_->generateWitness(); + const size_t v = mapFieldElementToInteger(0, registerLength, val(inputs_.arg1_val_)) * + mapFieldElementToInteger(0, registerLength, val(inputs_.arg2_val_)); + pb_->val(results_.result_) = mapIntegerToFieldElement(0, registerLength, v); + unpackResult_g_->generateWitness(); + for (size_t i = 0; i < registerLength; i++) { + pb_->val(witnessHighBits_[i]) = (v >> registerLength) & (size_t(1) << i) ? Algebra::one() : Algebra::zero(); + } + dmultPack_g_->generateWitness(); + //flag: + packHighBits_g_->generateWitness(); + if (Algebra::zero() == val(multPartials1_[0])) { + pb_->val(pInverse_) = Algebra::zero(); + pb_->val(results_.flag_) = Algebra::zero(); + } + else { + pb_->val(pInverse_) = (val(multPartials1_[0])).inverse(); + pb_->val(results_.flag_) = Algebra::one(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_UMULH_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_UMULH_Gadget::ALU_UMULH_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), + multPartials1_(opcodeAux1_), multPartials2_(opcodeAux2_), unpackedResult_(opcodeAux4_), + witnessLowBits_(opcodeAux3_), dmultPartials1_(opcodeAux5_), dmultPartials2_(opcodeAux6_){} + +GadgetPtr ALU_UMULH_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_UMULH_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_UMULH_Gadget::init(){ + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::UMULH); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::UMULH); + mult_g_ = Multiplication_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, multPartials1_, multPartials2_, false, Opcode::UMULH); + unpackResult_g_ = CompressionPacking_Gadget::create(pb_, unpackedResult_, results_.result_, PackingMode::UNPACK, Opcode::UMULH); + dmultPack_g_ = DoubleMultPack_Gadget::create(pb_, witnessLowBits_, unpackedResult_, dmultPartials1_, dmultPartials2_, multPartials2_[0], false, Opcode::UMULH); +} + +void ALU_UMULH_Gadget::generateConstraints(){ + if (standAlone_){ + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + } + mult_g_->generateConstraints(); + unpackResult_g_->generateConstraints(); + const size_t & registerLength = tinyRAMparams()->registerLength(); + for (unsigned int i = 0; i < registerLength; ++i) { + enforceBooleanity(witnessLowBits_[i], Opcode::UMULH); + } + dmultPack_g_->generateConstraints(); + //flag: + pb_->addGeneralConstraint(results_.result_ * (results_.result_ * pInverse_ + Algebra::one()), "flag inv", Opcode::UMULH); + pb_->addGeneralConstraint(results_.flag_ + results_.result_ * pInverse_, "umulh flag", Opcode::UMULH); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::UMULH); +} + +void ALU_UMULH_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + const size_t & registerLength = tinyRAMparams()->registerLength(); + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + mult_g_->generateWitness(); + const size_t v = mapFieldElementToInteger(0, registerLength, val(inputs_.arg1_val_)) * + mapFieldElementToInteger(0, registerLength, val(inputs_.arg2_val_)); + pb_->val(results_.result_) = mapIntegerToFieldElement(0, registerLength, v >> registerLength); + unpackResult_g_->generateWitness(); + for (size_t i = 0; i < registerLength; i++) { + pb_->val(witnessLowBits_[i]) = (v >> i) & size_t(1) ? Algebra::one() : Algebra::zero(); + } + dmultPack_g_->generateWitness(); + //flag: + if (Algebra::zero() == val(results_.result_)) { + pb_->val(pInverse_) = Algebra::zero(); + pb_->val(results_.flag_) = Algebra::zero(); + } + else { + pb_->val(pInverse_) = (val(results_.result_)).inverse(); + pb_->val(results_.flag_) = Algebra::one(); + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SMULH_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_SMULH_Gadget::ALU_SMULH_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), + multPartials1_(opcodeAux1_), multPartials2_(opcodeAux2_), unpackedResult_(opcodeAux4_), + witnessLowBits_(opcodeAux3_), dmultPartials1_(opcodeAux5_), dmultPartials2_(opcodeAux6_){} + +GadgetPtr ALU_SMULH_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_SMULH_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_SMULH_Gadget::init(){ + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::SMULH); + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::SMULH); + mult_g_ = Multiplication_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, multPartials1_, multPartials2_, true, Opcode::SMULH); + unpackResult_g_ = CompressionPacking_Gadget::create(pb_, unpackedResult_, results_.result_, PackingMode::UNPACK, Opcode::SMULH); + dmultPack_g_ = DoubleMultPack_Gadget::create(pb_, witnessLowBits_, unpackedResult_, dmultPartials1_, dmultPartials2_, multPartials2_[0], true, Opcode::SMULH); +} + +void ALU_SMULH_Gadget::generateConstraints(){ + if (standAlone_){ + unpackArg1_g_->generateConstraints(); + unpackArg2_g_->generateConstraints(); + } + mult_g_->generateConstraints(); + unpackResult_g_->generateConstraints(); + const size_t & registerLength = tinyRAMparams()->registerLength(); + for (unsigned int i = 0; i < registerLength; ++i) { + enforceBooleanity(witnessLowBits_[i], Opcode::SMULH); + } + dmultPack_g_->generateConstraints(); + //flag: + const Algebra::Variable invNegative = dmultPartials1_[0]; + const Algebra::FElem allOnes = mapIntegerToFieldElement(0, registerLength, 0xFFFF); + pb_->addGeneralConstraint(results_.result_ * (results_.result_ * pInverse_ + Algebra::one()), "flag positive inv", Opcode::SMULH); + pb_->addGeneralConstraint((results_.result_+allOnes) * ((results_.result_+allOnes) * invNegative + Algebra::one()), "flag negative inv", Opcode::SMULH); + pb_->addGeneralConstraint(results_.flag_ + results_.result_*pInverse_ + (results_.result_+allOnes)*invNegative + Algebra::one(), "flag", Opcode::SMULH); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::SMULH); +} + +void ALU_SMULH_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + const size_t & registerLength = tinyRAMparams()->registerLength(); + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + mult_g_->generateWitness(); + const int32_t v = int16_t(mapFieldElementToInteger(0, registerLength, val(inputs_.arg1_val_))) * + int16_t(mapFieldElementToInteger(0, registerLength, val(inputs_.arg2_val_))); + pb_->val(results_.result_) = mapIntegerToFieldElement(0, registerLength, v >> registerLength); + unpackResult_g_->generateWitness(); + for (unsigned int i = 0; i < registerLength; i++) { + pb_->val(witnessLowBits_[i]) = (v >> i) & 1 ? Algebra::one() : Algebra::zero(); + } + dmultPack_g_->generateWitness(); + //flag: + const Algebra::Variable invNegative = dmultPartials1_[0]; + const Algebra::FElem allOnes = mapIntegerToFieldElement(0, registerLength, 0xFFFF); + if (Algebra::zero() != val(results_.result_)) { + pb_->val(pInverse_) = (val(results_.result_)).inverse(); + if (allOnes != val(results_.result_)) { + pb_->val(invNegative) = (val(results_.result_) + allOnes).inverse(); + pb_->val(results_.flag_) = Algebra::one(); + return; + } + pb_->val(results_.flag_) = Algebra::zero(); + return; + } + pb_->val(invNegative) = (val(results_.result_) + allOnes).inverse(); + pb_->val(results_.flag_) = Algebra::zero(); +} + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_UDIV_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_UDIV_Gadget::ALU_UDIV_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) : + Gadget(pb), + ALU_Component_Gadget(pb, inputs, results), + cmpFlags_(opcodeAux7_), + isGEQ_(opcodeAux7_[0]), + unpackedResult_(opcodeAux3_), + multPartials1_(opcodeAux1_), + multPartials2_(opcodeAux2_), + mpartialsArg1_(opcodeAux4_), + mpackArg1_(opcodeAux4_[REGISTER_LENGTH - 1]), + mpartialsRemainder_(opcodeAux6_), + mpackRemainder_(opcodeAux6_[REGISTER_LENGTH - 1]), + witnessRemainder_(opcodeAux5_){}; + +GadgetPtr ALU_UDIV_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_UDIV_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_UDIV_Gadget::init(){ + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::UDIV); + unpackResult_g_ = CompressionPacking_Gadget::create(pb_, unpackedResult_, results_.result_, PackingMode::UNPACK, Opcode::UDIV); + mult_g_ = Multiplication_Gadget::create(pb_, unpackedArg2_, unpackedResult_, multPartials1_, multPartials2_, false, Opcode::UDIV); + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::UDIV); + multPackArg1_g_ = MultiplicationPacking_Gadget::create(pb_, unpackedArg1_, mpartialsArg1_, mpackArg1_, false, PackingMode::PACK, Opcode::UDIV); + multPackRemainder_g_ = MultiplicationPacking_Gadget::create(pb_, witnessRemainder_, mpartialsRemainder_, mpackRemainder_, false, PackingMode::PACK, Opcode::UDIV); + GEQ_g_ = GreaterEqual_Gadget::create(pb_, unpackedArg2_, witnessRemainder_, cmpFlags_, isGEQ_, false, Opcode::UDIV); +} + +void ALU_UDIV_Gadget::generateConstraints(){ + if (standAlone_) + unpackArg2_g_->generateConstraints(); + unpackResult_g_->generateConstraints(); + const size_t & registerLength = tinyRAMparams()->registerLength(); + for (size_t i = 0; i < registerLength; i++) + enforceBooleanity(witnessRemainder_[i], Opcode::UMOD); + mult_g_->generateConstraints(); + if (standAlone_) + unpackArg1_g_->generateConstraints(); + multPackArg1_g_->generateConstraints(); + multPackRemainder_g_->generateConstraints(); + pb_->addGeneralConstraint(inputs_.arg2_val_ * (inputs_.arg2_val_ * pInverse_ + Algebra::one()), "divisor inv", Opcode::UDIV); + pb_->addGeneralConstraint( + inputs_.arg2_val_ * (multPartials2_[0] + mpackArg1_ * mpackRemainder_) + + (inputs_.arg2_val_ * pInverse_ + Algebra::one()) * results_.result_, + "dividor!=0 -> res*divisor + remainder == arg1", Opcode::UDIV); + pb_->addGeneralConstraint(inputs_.arg2_val_ * (isGEQ_ + Algebra::one()), "divisor!=0 -> remainderaddGeneralConstraint(results_.flag_ + inputs_.arg2_val_ * pInverse_ + Algebra::one(), "udiv flag", Opcode::UDIV); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::UDIV); +} + +void ALU_UDIV_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + const size_t & registerLength = tinyRAMparams()->registerLength(); + const size_t operand2 = mapFieldElementToInteger(0, registerLength, val(inputs_.arg2_val_)); + if (0 == operand2){ + pb_->val(pInverse_) = Algebra::zero(); //needed? + pb_->val(results_.result_) = Algebra::zero(); + pb_->val(results_.flag_) = Algebra::one(); + } + else { + pb_->val(pInverse_) = (val(inputs_.arg2_val_)).inverse(); + const size_t operand1 = mapFieldElementToInteger(0, registerLength, val(inputs_.arg1_val_)); + val(results_.result_) = mapIntegerToFieldElement(0, registerLength, operand1 / operand2); + const size_t rem = operand1 % operand2; + for (size_t i = 0; i < registerLength; i++) + pb_->val(witnessRemainder_[i]) = (rem >> i) & size_t(1) ? Algebra::one() : Algebra::zero(); + pb_->val(results_.flag_) = Algebra::zero(); + } + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + unpackResult_g_->generateWitness(); + mult_g_->generateWitness(); + std::dynamic_pointer_cast(multPackArg1_g_)->generateWitness(); + std::dynamic_pointer_cast(multPackRemainder_g_)->generateWitness(); + GEQ_g_->generateWitness(); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_UMOD_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_UMOD_Gadget::ALU_UMOD_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) : + Gadget(pb), + ALU_Component_Gadget(pb, inputs, results), + cmpFlags_(opcodeAux7_), + isGEQ_(opcodeAux7_[0]), + multPartials1_(opcodeAux1_), + multPartials2_(opcodeAux2_), + mpartialsArg1_(opcodeAux4_), + mpackArg1_(opcodeAux4_[REGISTER_LENGTH - 1]), + mpartialsRemainder_(opcodeAux6_), + mpackRemainder_(opcodeAux6_[REGISTER_LENGTH - 1]), + witnessRemainder_(opcodeAux5_), + witnessResult_(opcodeAux3_){}; + +GadgetPtr ALU_UMOD_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_UMOD_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_UMOD_Gadget::init(){ + unpackArg2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::UMOD); + mult_g_ = Multiplication_Gadget::create(pb_, unpackedArg2_, witnessResult_, multPartials1_, multPartials2_, false, Opcode::UMOD); + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK, Opcode::UMOD); + multPackArg1_g_ = MultiplicationPacking_Gadget::create(pb_, unpackedArg1_, mpartialsArg1_, mpackArg1_, false, PackingMode::PACK, Opcode::UMOD); + multPackRemainder_g_ = MultiplicationPacking_Gadget::create(pb_, witnessRemainder_, mpartialsRemainder_, mpackRemainder_, false, PackingMode::PACK, Opcode::UMOD); + GEQ_g_ = GreaterEqual_Gadget::create(pb_, unpackedArg2_, witnessRemainder_, cmpFlags_, isGEQ_, false, Opcode::UMOD); + packResult_g_ = CompressionPacking_Gadget::create(pb_, witnessRemainder_, results_.result_, PackingMode::PACK, Opcode::UMOD); +} + +void ALU_UMOD_Gadget::generateConstraints(){ + if (standAlone_) + unpackArg2_g_->generateConstraints(); + const size_t & registerLength = tinyRAMparams()->registerLength(); + for (size_t i = 0; i < registerLength; i++) { + enforceBooleanity(witnessResult_[i], Opcode::UMOD); + enforceBooleanity(witnessRemainder_[i], Opcode::UMOD); + } + mult_g_->generateConstraints(); + if (standAlone_) + unpackArg1_g_->generateConstraints(); + multPackArg1_g_->generateConstraints(); + multPackRemainder_g_->generateConstraints(); + pb_->addGeneralConstraint(inputs_.arg2_val_ * (inputs_.arg2_val_ * pInverse_ + Algebra::one()), "divisor inv", Opcode::UMOD); + pb_->addGeneralConstraint( + inputs_.arg2_val_ * (multPartials2_[0] + mpackArg1_ * mpackRemainder_) + + (inputs_.arg2_val_ * pInverse_ + Algebra::one()) * results_.result_, + "dividor!=0 -> res*divisor + remainder == arg1", Opcode::UMOD); + pb_->addGeneralConstraint(inputs_.arg2_val_ * (isGEQ_ + Algebra::one()), "divisor!=0 -> remainderaddGeneralConstraint(results_.flag_ + inputs_.arg2_val_ * pInverse_ + Algebra::one(), "umod flag", Opcode::UMOD); + packResult_g_->generateConstraints(); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::UMOD); +} + +void ALU_UMOD_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + const size_t & registerLength = tinyRAMparams()->registerLength(); + const size_t operand2 = mapFieldElementToInteger(0, registerLength, val(inputs_.arg2_val_)); + if (0 == operand2){ + pb_->val(pInverse_) = Algebra::zero(); //needed? + pb_->val(results_.result_) = Algebra::zero(); + pb_->val(results_.flag_) = Algebra::one(); + } + else { + pb_->val(pInverse_) = (val(inputs_.arg2_val_)).inverse(); + const size_t operand1 = mapFieldElementToInteger(0, registerLength, val(inputs_.arg1_val_)); + const size_t result = operand1 / operand2; + const size_t rem = operand1 % operand2; + val(results_.result_) = mapIntegerToFieldElement(0, registerLength, rem); + for (size_t i = 0; i < registerLength; i++) { + pb_->val(witnessResult_[i]) = (result >> i) & size_t(1) ? Algebra::one() : Algebra::zero(); + pb_->val(witnessRemainder_[i]) = (rem >> i) & size_t(1) ? Algebra::one() : Algebra::zero(); + } + pb_->val(results_.flag_) = Algebra::zero(); + } + unpackArg1_g_->generateWitness(); + unpackArg2_g_->generateWitness(); + mult_g_->generateWitness(); + std::dynamic_pointer_cast(multPackArg1_g_)->generateWitness(); + std::dynamic_pointer_cast(multPackRemainder_g_)->generateWitness(); + GEQ_g_->generateWitness(); + packResult_g_->generateWitness(); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPE_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_CMPE_Gadget::ALU_CMPE_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_CMPE_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_CMPE_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_CMPE_Gadget::init(){} + +void ALU_CMPE_Gadget::generateConstraints(){ + CircuitPolynomial flagConstarint1((inputs_.arg1_val_ + inputs_.arg2_val_) * results_.flag_); + CircuitPolynomial flagConstarint2((inputs_.arg1_val_ + inputs_.arg2_val_)* pInverse_); + CircuitPolynomial flagConstraint3(Algebra::one() + results_.flag_); + pb_->addGeneralConstraint(flagConstarint1, "flag * (arg1_val + arg2_val)= 0", Opcode::NOT); + pb_->addGeneralConstraint(flagConstarint2 + flagConstraint3, "(arg1_val + arg2_val) * invResult = 1 - flag", Opcode::CMPE); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::CMPE); +} + +void ALU_CMPE_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + FElem arg1Val = pb_->val(inputs_.arg1_val_); + FElem arg2Val = pb_->val(inputs_.arg2_val_); + pb_->val(results_.flag_) = arg1Val == arg2Val ? Algebra::one() : Algebra::zero(); + pb_->val(pInverse_) = arg1Val == arg2Val ? Algebra::one() : (arg1Val + arg2Val).inverse(); + pb_->val(results_.result_) = Algebra::zero(); // We don't care which value result_ holds - needed for the coloring +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPA_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_CMPA_Gadget::ALU_CMPA_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), + cmpFlags_(opcodeAux2_), isGEQ_(opcodeAux2_[0]){} + +GadgetPtr ALU_CMPA_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_CMPA_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_CMPA_Gadget::init(){ + unpack1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK ,Opcode::CMPA); + unpack2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK ,Opcode::CMPA); + compareArgs_ = GreaterEqual_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, cmpFlags_, isGEQ_, false, Opcode::CMPA); +} + +void ALU_CMPA_Gadget::generateConstraints(){ + if (standAlone_){ + unpack1_g_->generateConstraints(); + unpack2_g_->generateConstraints(); + } + compareArgs_->generateConstraints(); + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + const Algebra::FElem inv = (Algebra::one() + g).inverse(); + CircuitPolynomial c(results_.flag_ + isGEQ_*(isGEQ_ + g)*inv); + pb_->addGeneralConstraint(c, "flag=isGEQ*(isGEQ+g)*(1+g)^{-1}", Opcode::CMPA); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::CMPA); +} + +void ALU_CMPA_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpack1_g_->generateWitness(); + unpack2_g_->generateWitness(); + compareArgs_->generateWitness(); + FElem arg1Val = pb_->val(inputs_.arg1_val_); + FElem arg2Val = pb_->val(inputs_.arg2_val_); + pb_->val(results_.flag_) = (Algebra::one()==val(isGEQ_)) ? Algebra::one() : Algebra::zero(); + pb_->val(results_.result_) = Algebra::zero(); // We don't care which value result_ holds - needed for the coloring + +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPAE_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_CMPAE_Gadget::ALU_CMPAE_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), + cmpFlags_(opcodeAux2_), isGEQ_(opcodeAux2_[0]){} + +GadgetPtr ALU_CMPAE_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_CMPAE_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_CMPAE_Gadget::init(){ + unpack1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK ,Opcode::CMPAE); + unpack2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK ,Opcode::CMPAE); + compareArgs_ = GreaterEqual_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, cmpFlags_, isGEQ_, false, Opcode::CMPAE); +} + +void ALU_CMPAE_Gadget::generateConstraints(){ + if (standAlone_){ + unpack1_g_->generateConstraints(); + unpack2_g_->generateConstraints(); + } + compareArgs_->generateConstraints(); + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + const Algebra::FElem inv1 = (Algebra::one() + g).inverse(); + const Algebra::FElem inv2 = g.inverse() * inv1; + CircuitPolynomial c(results_.flag_ + isGEQ_*(isGEQ_ + g)*inv1 + isGEQ_*(isGEQ_ + Algebra::one())*inv2); + pb_->addGeneralConstraint(c, "flag=isGEQ*(isGEQ+g)*(1+g)^{-1}+isGEQ*(isGEQ+1)*(g(1+g))^{-1}", Opcode::CMPAE); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::CMPAE); +} + +void ALU_CMPAE_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpack1_g_->generateWitness(); + unpack2_g_->generateWitness(); + compareArgs_->generateWitness(); + FElem flag = pb_->val(isGEQ_); + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + pb_->val(results_.flag_) = ((Algebra::one()==flag)||(g==flag)) ? Algebra::one() : Algebra::zero(); + pb_->val(results_.result_) = Algebra::zero(); // We don't care which value result_ holds - needed for the coloring +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPG_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_CMPG_Gadget::ALU_CMPG_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), + cmpFlags_(opcodeAux2_), isGEQ_(opcodeAux2_[0]){} + +GadgetPtr ALU_CMPG_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_CMPG_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_CMPG_Gadget::init(){ + unpack1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK ,Opcode::CMPG); + unpack2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK ,Opcode::CMPG); + compareArgs_ = GreaterEqual_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, cmpFlags_, isGEQ_, true, Opcode::CMPG); +} + +void ALU_CMPG_Gadget::generateConstraints(){ + if (standAlone_){ + unpack1_g_->generateConstraints(); + unpack2_g_->generateConstraints(); + } + compareArgs_->generateConstraints(); + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + const Algebra::FElem inv = (Algebra::one() + g).inverse(); + CircuitPolynomial c(results_.flag_ + isGEQ_*(isGEQ_ + g)*inv); + pb_->addGeneralConstraint(c, "flag=isGEQ*(isGEQ+g)*(1+g)^{-1}", Opcode::CMPG); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::CMPG); +} + +void ALU_CMPG_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpack1_g_->generateWitness(); + unpack2_g_->generateWitness(); + compareArgs_->generateWitness(); + FElem arg1Val = pb_->val(inputs_.arg1_val_); + FElem arg2Val = pb_->val(inputs_.arg2_val_); + pb_->val(results_.flag_) = (Algebra::one()==val(isGEQ_)) ? Algebra::one() : Algebra::zero(); + pb_->val(results_.result_) = Algebra::zero(); // We don't care which value result_ holds - needed for the coloring + +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPGE_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_CMPGE_Gadget::ALU_CMPGE_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), + cmpFlags_(opcodeAux2_), isGEQ_(opcodeAux2_[0]){} + +GadgetPtr ALU_CMPGE_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_CMPGE_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_CMPGE_Gadget::init(){ + unpack1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg1_val_, PackingMode::UNPACK ,Opcode::CMPGE); + unpack2_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, inputs_.arg2_val_, PackingMode::UNPACK ,Opcode::CMPGE); + compareArgs_ = GreaterEqual_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, cmpFlags_, isGEQ_, true, Opcode::CMPGE); +} + +void ALU_CMPGE_Gadget::generateConstraints(){ + if (standAlone_){ + unpack1_g_->generateConstraints(); + unpack2_g_->generateConstraints(); + } + compareArgs_->generateConstraints(); + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + const Algebra::FElem inv1 = (Algebra::one() + g).inverse(); + const Algebra::FElem inv2 = g.inverse() * inv1; + CircuitPolynomial c(results_.flag_ + isGEQ_*(isGEQ_ + g)*inv1 + isGEQ_*(isGEQ_ + Algebra::one())*inv2); + pb_->addGeneralConstraint(c, "flag=isGEQ*(isGEQ+g)*(1+g)^{-1}+isGEQ*(isGEQ+1)*(g(1+g))^{-1}", Opcode::CMPGE); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::CMPGE); +} + +void ALU_CMPGE_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpack1_g_->generateWitness(); + unpack2_g_->generateWitness(); + compareArgs_->generateWitness(); + FElem flag = pb_->val(isGEQ_); + const Algebra::FElem g = Algebra::FElem(getGF2E_X()); + pb_->val(results_.flag_) = ((Algebra::one()==flag)||(g==flag)) ? Algebra::one() : Algebra::zero(); + pb_->val(results_.result_) = Algebra::zero(); // We don't care which value result_ holds - needed for the coloring +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SHL_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_SHL_Gadget::ALU_SHL_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), auxArr_(opcodeAux1_){} + +GadgetPtr ALU_SHL_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_SHL_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_SHL_Gadget::init(){ + size_t registerLength = tinyRAMparams()->registerLength(); + unpackDouble_g_ = DoubleUnpack_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, + auxArr_[registerLength - ALU_SHL_Gadget::idxs::dRes], Opcode::SHL); + packResult_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, results_.result_, PackingMode::PACK, Opcode::SHL); +} + +void ALU_SHL_Gadget::generateConstraints(){ + const long registerLength = long((tinyRAMparams()->registerLength())); + const long registerLogLen = long(log2(registerLength)); + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + Algebra::CircuitPolynomial tmpPoly, witnessPoly; + Algebra::LinearCombination tmpLC; + const Variable remainder = auxArr_[registerLength-ALU_SHL_Gadget::idxs::rem]; + Algebra::FElem x_i = Algebra::one(); + for (int i = 0; i < registerLogLen; ++i){ + const Variable v = auxArr_[i]; + const Variable w = auxArr_[i+registerLogLen]; + tmpLC = tmpLC + x_i * v; + x_i *= x; + enforceBooleanity(v, Opcode::SHL); + witnessPoly = Algebra::invExtrConsts[i] * (w*w + w) + remainder; + pb_->addGeneralConstraint(witnessPoly, "traceWitness", Opcode::SHL); + } + tmpLC = tmpLC + remainder; + pb_->addGeneralConstraint(tmpLC + inputs_.arg2_val_, "[A] = tmpPoly", Opcode::SHL); + + const Variable wFlag = auxArr_[registerLength-ALU_SHL_Gadget::idxs::witnessFlag]; + witnessPoly = Algebra::invExtrConsts[registerLength-1] * (wFlag*wFlag + wFlag) + + inputs_.arg1_val_ + Algebra::power(x, registerLength-1) * results_.flag_; + pb_->addGeneralConstraint(witnessPoly, "flagWitness", Opcode::SHL); + enforceBooleanity(results_.flag_, Opcode::SHL); + + tmpPoly = inputs_.arg1_val_; + x_i = x; + for (int i = 0; i < registerLogLen; ++i){ + const Variable v = auxArr_[i]; + tmpPoly = tmpPoly * (Algebra::one() + v + v * x_i); + x_i *= x_i; + } + const Variable invRem = auxArr_[registerLength-ALU_SHL_Gadget::idxs::invRem]; + tmpPoly = tmpPoly * (remainder * invRem + Algebra::one()); + const Variable doubleResult = auxArr_[registerLength-ALU_SHL_Gadget::idxs::dRes]; + pb_->addGeneralConstraint(tmpPoly + doubleResult, "doubleResult = tmpPoly", Opcode::SHL); + + tmpPoly = remainder * (remainder * invRem + Algebra::one()); + pb_->addGeneralConstraint(tmpPoly, "rem*(rem * invRem + 1)", Opcode::SHL); + if (standAlone_) + unpackDouble_g_->generateConstraints(); + packResult_g_->generateConstraints(); + + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::SHL); +} + +void ALU_SHL_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + const long registerLength = long((tinyRAMparams()->registerLength())); + const long registerLogLen = long(log2(registerLength)); + + size_t operand2 = Algebra::mapFieldElementToInteger(0, EXTDIM, pb_->val(inputs_.arg2_val_)); + size_t r = operand2; + for (int i = 0; i < registerLogLen; ++i){ + const Variable v = auxArr_[i]; + size_t mask = size_t(1) << i; + pb_->val(v) = (r & mask) ? Algebra::one() : Algebra::zero(); + r = r & (~mask); + } + const Algebra::FElem remainder = Algebra::mapIntegerToFieldElement(0, EXTDIM, r); + pb_->val(auxArr_[registerLength - ALU_SHL_Gadget::idxs::rem]) = remainder; + + for (int i = 0; i < registerLogLen; ++i){ + const Variable w = auxArr_[i+registerLogLen]; + pb_->val(w) = extractBit(remainder, i); + } + + const FElem operand1 = pb_->val(inputs_.arg1_val_); + size_t doubleRes = Algebra::mapFieldElementToInteger(0, EXTDIM, operand1); + const FElem fToggled = Algebra::mapIntegerToFieldElement(0, EXTDIM, + doubleRes & (~(size_t(1) << (registerLength - 1)))); + //const FElem fRes = Algebra::mapIntegerToFieldElement(0, 1, doubleRes >> (registerLength - 1)); + const FElem fRes = (doubleRes >> (registerLength - 1)) ? Algebra::one() : Algebra::zero(); + pb_->val(results_.flag_) = fRes; + pb_->val(auxArr_[registerLength - ALU_SHL_Gadget::idxs::witnessFlag]) = + extractBit(fToggled, registerLength-1 /* MSB */); + + if (r) { + pb_->val(auxArr_[registerLength - ALU_SHL_Gadget::idxs::invRem]) = remainder.inverse(); + pb_->val(auxArr_[registerLength - ALU_SHL_Gadget::idxs::dRes]) = Algebra::zero(); + } + else { + pb_->val(auxArr_[registerLength - ALU_SHL_Gadget::idxs::invRem]) = Algebra::zero(); + doubleRes = ((long)operand2 >= registerLength ? 0 : (doubleRes << operand2)); + pb_->val(auxArr_[registerLength - ALU_SHL_Gadget::idxs::dRes]) = Algebra::mapIntegerToFieldElement(0, EXTDIM, doubleRes); + } + unpackDouble_g_->generateWitness(); + packResult_g_->generateWitness(); + +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SHR_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_SHR_Gadget::ALU_SHR_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results), auxArr_(opcodeAux1_){} + +GadgetPtr ALU_SHR_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_SHR_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_SHR_Gadget::init(){ + size_t registerLength = tinyRAMparams()->registerLength(); + unpackDouble_g_ = DoubleUnpack_Gadget::create(pb_, unpackedArg1_, unpackedArg2_, + auxArr_[registerLength - ALU_SHR_Gadget::idxs::dRes], Opcode::SHR); + packResult_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg2_, results_.result_, PackingMode::PACK, Opcode::SHR); +} + +void ALU_SHR_Gadget::generateConstraints(){ + const long registerLength = long((tinyRAMparams()->registerLength())); + const long registerLogLen = long(log2(registerLength)); + const Algebra::FElem x = Algebra::FElem(getGF2E_X()); + const Algebra::FElem invx = x.inverse(); + Algebra::CircuitPolynomial tmpPoly, witnessPoly; + + const Variable remainder = auxArr_[registerLength-ALU_SHR_Gadget::idxs::rem]; + Algebra::FElem x_i = Algebra::one(); + for (int i = 0; i < registerLogLen; ++i){ + const Variable v = auxArr_[i]; + const Variable w = auxArr_[i+registerLogLen]; + tmpPoly = tmpPoly + x_i * v; + x_i *= x; + enforceBooleanity(v, Opcode::SHR); + witnessPoly = Algebra::invExtrConsts[i] * (w*w + w) + remainder; + pb_->addGeneralConstraint(witnessPoly, "traceWitness", Opcode::SHR); + } + tmpPoly = tmpPoly + remainder; + pb_->addGeneralConstraint(tmpPoly + inputs_.arg2_val_, "[A] = tmpPoly", Opcode::SHR); + + const Variable wFlag = auxArr_[registerLength-ALU_SHR_Gadget::idxs::witnessFlag]; + witnessPoly = Algebra::invExtrConsts[0] * (wFlag*wFlag + wFlag) + inputs_.arg1_val_ + results_.flag_; + pb_->addGeneralConstraint(witnessPoly, "flagWitness", Opcode::SHR); + enforceBooleanity(results_.flag_, Opcode::SHR); + + tmpPoly = Algebra::power(x, registerLength) * inputs_.arg1_val_; + x_i = invx; + for (int i = 0; i < registerLogLen; ++i){ + const Variable v = auxArr_[i]; + tmpPoly = tmpPoly * (Algebra::one() + v + v * x_i); + x_i *= x_i; + } + const Variable invRem = auxArr_[registerLength-ALU_SHR_Gadget::idxs::invRem]; + tmpPoly = tmpPoly * (remainder * invRem + Algebra::one()); + const Variable doubleResult = auxArr_[registerLength-ALU_SHR_Gadget::idxs::dRes]; + pb_->addGeneralConstraint(tmpPoly + doubleResult, "doubleResult = tmpPoly", Opcode::SHR); + + tmpPoly = remainder * (remainder * invRem + Algebra::one()); + pb_->addGeneralConstraint(tmpPoly, "rem*(rem * invRem + 1)", Opcode::SHR); + + if (standAlone_) + unpackDouble_g_->generateConstraints(); + packResult_g_->generateConstraints(); + // add isMemOp = 0 + pb_->addGeneralConstraint(results_.isMemOp_, "isMemOp = 0", Opcode::SHR); +} + +void ALU_SHR_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + const long registerLength = long((tinyRAMparams()->registerLength())); + const long registerLogLen = long(log2(registerLength)); + + size_t operand2 = Algebra::mapFieldElementToInteger(0, EXTDIM, pb_->val(inputs_.arg2_val_)); + size_t r = operand2; + for (int i = 0; i < registerLogLen; ++i){ + const Variable v = auxArr_[i]; + size_t mask = size_t(1) << i; + pb_->val(v) = (r & mask) ? Algebra::one() : Algebra::zero(); + r = r & (~mask); + } + const Algebra::FElem remainder = Algebra::mapIntegerToFieldElement(0, EXTDIM, r); + pb_->val(auxArr_[registerLength - ALU_SHR_Gadget::idxs::rem]) = remainder; + + for (int i = 0; i < registerLogLen; ++i){ + const Variable w = auxArr_[i+registerLogLen]; + pb_->val(w) = extractBit(remainder, i); + } + + const FElem operand1 = pb_->val(inputs_.arg1_val_); + size_t doubleRes = Algebra::mapFieldElementToInteger(0, EXTDIM, operand1); + + const FElem fRes = Algebra::mapIntegerToFieldElement(0, 1, doubleRes); + pb_->val(results_.flag_) = fRes; + pb_->val(auxArr_[registerLength - ALU_SHR_Gadget::idxs::witnessFlag]) = + extractBit(operand1+fRes, 0 /* LSB */); + + if (r) { + pb_->val(auxArr_[registerLength - ALU_SHR_Gadget::idxs::invRem]) = remainder.inverse(); + pb_->val(auxArr_[registerLength - ALU_SHR_Gadget::idxs::dRes]) = Algebra::zero(); + } + else { + pb_->val(auxArr_[registerLength - ALU_SHR_Gadget::idxs::invRem]) = Algebra::zero(); + doubleRes = ((long)operand2 >= registerLength ? 0 : (doubleRes << (registerLength - operand2))); + pb_->val(auxArr_[registerLength - ALU_SHR_Gadget::idxs::dRes]) = Algebra::mapIntegerToFieldElement(0, EXTDIM, doubleRes); + } + + unpackDouble_g_->generateWitness(); + packResult_g_->generateWitness(); + +} + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_JMP_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_JMP_Gadget::ALU_JMP_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + : Gadget(pb), ALU_Component_Gadget(pb, inputs, results) {} + +GadgetPtr ALU_JMP_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_JMP_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_JMP_Gadget::init(){} + +void ALU_JMP_Gadget::generateConstraints(){ + pb_->addGeneralConstraint(results_.flag_ + inputs_.flag_, "inputs.flag_ = results.flag_", Opcode::JMP); + pb_->addGeneralConstraint(results_.result_ + inputs_.arg2_val_, "inputs.arg2_val = result.result", Opcode::JMP); +} + +void ALU_JMP_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + pb_->val(results_.flag_) = pb_->val(inputs_.flag_); + pb_->val(results_.result_) = pb_->val(inputs_.arg2_val_); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CJMP_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_CJMP_Gadget::ALU_CJMP_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + : Gadget(pb), ALU_Component_Gadget(pb, inputs, results) {} + +GadgetPtr ALU_CJMP_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_CJMP_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_CJMP_Gadget::init(){} + +void ALU_CJMP_Gadget::generateConstraints(){ + pb_->addGeneralConstraint(results_.flag_ + inputs_.flag_, "inputs.flag_ = results.flag_", Opcode::CJMP); + pb_->addGeneralConstraint(results_.result_ + inputs_.arg2_val_, "inputs.arg2_val = result.result", Opcode::CJMP); +} + +void ALU_CJMP_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + pb_->val(results_.flag_) = pb_->val(inputs_.flag_); + pb_->val(results_.result_) = pb_->val(inputs_.arg2_val_); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CNJMP_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_CNJMP_Gadget::ALU_CNJMP_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + : Gadget(pb), ALU_Component_Gadget(pb, inputs, results) {} + +GadgetPtr ALU_CNJMP_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_CNJMP_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_CNJMP_Gadget::init(){} + +void ALU_CNJMP_Gadget::generateConstraints(){ + pb_->addGeneralConstraint(results_.flag_ + inputs_.flag_, "inputs.flag_ = results.flag_", Opcode::CNJMP); + pb_->addGeneralConstraint(results_.result_ + inputs_.arg2_val_, "inputs.arg2_val = result.result", Opcode::CNJMP); +} + +void ALU_CNJMP_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + pb_->val(results_.flag_) = pb_->val(inputs_.flag_); + pb_->val(results_.result_) = pb_->val(inputs_.arg2_val_); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_STOREW_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +ALU_STOREW_Gadget::ALU_STOREW_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_STOREW_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_STOREW_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_STOREW_Gadget::init(){} + +void ALU_STOREW_Gadget::generateConstraints(){ + pb_->addGeneralConstraint(results_.isMemOp_ + Algebra::one(), "isMemOp = 1", Opcode::STOREW); + pb_->addGeneralConstraint(results_.isLoadOp_, "isLoadOp = 0", Opcode::STOREW); + pb_->addGeneralConstraint(inputs_.flag_ + results_.flag_, "inputs_.flag = results.flag_", Opcode::STOREW); + pb_->addGeneralConstraint(results_.value_ + inputs_.dest_val_, "inputs_.value_ = inputs_.dest_val", Opcode::STOREW); +} + +void ALU_STOREW_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + pb_->val(results_.isMemOp_) = Algebra::one(); + pb_->val(results_.isLoadOp_) = Algebra::zero(); + FElem memoryAddress = pb_->val(inputs_.arg2_val_); + FElem value = pb_->val(inputs_.dest_val_); + // Stores arg2 To dest_val + pb_->storeValue(memoryAddress, value); + pb_->val(results_.value_) = value; + pb_->val(results_.address_) = memoryAddress; + pb_->val(results_.flag_) = pb_->val(inputs_.flag_); +} + +/*********************************/ +/*** END OF Gadget ***/ +/*********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_LOADW_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +ALU_LOADW_Gadget::ALU_LOADW_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) +:Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_LOADW_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_LOADW_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_LOADW_Gadget::init(){} + +void ALU_LOADW_Gadget::generateConstraints(){ + pb_->addGeneralConstraint(results_.isMemOp_ + Algebra::one(), "isMemOp = 1", Opcode::LOADW); + pb_->addGeneralConstraint(results_.isLoadOp_ + Algebra::one(), "isLoadOp = 1", Opcode::LOADW); + pb_->addGeneralConstraint(inputs_.flag_ + results_.flag_, "inputs_.flag = results.flag_", Opcode::LOADW); +} + +void ALU_LOADW_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + pb_->val(results_.isLoadOp_) = Algebra::one(); + pb_->val(results_.isMemOp_) = Algebra::one(); + FElem address = pb_->val(inputs_.arg2_val_); // stores [A] to r_1 - check traceConsistency + FElem value = pb_->loadValue(address); + pb_->val(results_.address_) = address; + pb_->val(results_.value_) = value; + pb_->val(results_.flag_) = pb_->val(inputs_.flag_); +} + + +/*********************************/ +/*** END OF Gadget ***/ +/*********************************/ + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_ANSWER_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_ANSWER_Gadget::ALU_ANSWER_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + : Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_ANSWER_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_ANSWER_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} +void ALU_ANSWER_Gadget::init(){} +void ALU_ANSWER_Gadget::generateConstraints(){ + if (max_timestep > 1) { + pb_->addBoundaryConstraint(inputs_.arg2_val_, max_timestep, program_output); + } +} +void ALU_ANSWER_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + static bool flag = true; + if (flag) { + flag = false; + if (Algebra::one() == program_output) + program_output = pb_->val(inputs_.arg2_val_); + /* + * size_t a = mapFieldElementToInteger(0, EXTDIM, pb_->val(inputs_.arg2_val_)); + * std::cout << "\n*** TIMESTEPS=" << max_timestep << " ANSWER=" << a << " (binary " << std::bitset(a) << ")\n" << std::endl; + */ + } +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_MOV_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_MOV_Gadget::ALU_MOV_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + : Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_MOV_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_MOV_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_MOV_Gadget::init(){} + +void ALU_MOV_Gadget::generateConstraints(){ + pb_->addGeneralConstraint(inputs_.flag_ + results_.flag_, "inputs_.flag = results.flag_", Opcode::MOV); + pb_->addGeneralConstraint(inputs_.arg2_val_ + results_.result_, "results.result = inputs.arg2_val", Opcode::MOV); +} +void ALU_MOV_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + pb_->val(results_.flag_) = pb_->val(inputs_.flag_); + pb_->val(results_.result_) = pb_->val(inputs_.arg2_val_); +} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_RESERVED_OPCODE_24_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALU_RESERVED_OPCODE_24_Gadget::ALU_RESERVED_OPCODE_24_Gadget(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results) + :Gadget(pb), ALU_Component_Gadget(pb, inputs, results){} + +GadgetPtr ALU_RESERVED_OPCODE_24_Gadget::create(ProtoboardPtr pb, const ALUInput& inputs, const ALUOutput& results){ + GadgetPtr pGadget(new ALU_RESERVED_OPCODE_24_Gadget(pb, inputs, results)); + pGadget->init(); + return pGadget; +} + +void ALU_RESERVED_OPCODE_24_Gadget::init(){ + unpackArg1_g_ = CompressionPacking_Gadget::create(pb_, unpackedArg1_, inputs_.arg2_val_, PackingMode::UNPACK, Opcode::RESERVED_OPCODE_24); +} + +void ALU_RESERVED_OPCODE_24_Gadget::generateConstraints(){ + if (standAlone_) { + unpackArg1_g_->generateConstraints(); + } + srand(prngseed); + rand(); + CircuitPolynomial t, rPoly = Algebra::zero(); + for (unsigned int i = 0; i < ROMSIZE; ++i){ + t = Algebra::one(); + for (unsigned int k=0,e=1; e> k)) + t = t * unpackedArg1_[k]; + else + t = t * (unpackedArg1_[k] + Algebra::one()); + rPoly = rPoly + t * (results_.result_ + mapIntegerToFieldElement(0, REGISTER_LENGTH, rand()-RAND_MAX/2)); + } + pb_->addGeneralConstraint(rPoly, "ROM", Opcode::RESERVED_OPCODE_24); +} + +void ALU_RESERVED_OPCODE_24_Gadget::generateWitness(){ + initGeneralOpcodes(pb_); + initMemResult(pb_, results_); + unpackArg1_g_->generateWitness(); + srand(prngseed); rand(); + size_t n = mapFieldElementToInteger(0, EXTDIM, val(inputs_.arg2_val_)); + if (n < ROMSIZE) + for (unsigned int i = 0; i < n; ++i) + (void)rand(); //lame, replace with md5 or sha + unsigned int res = rand() - RAND_MAX/2; + val(results_.result_) = mapIntegerToFieldElement(0, REGISTER_LENGTH, res); +} + + + diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALU.hpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALU.hpp new file mode 100644 index 0000000..7dba5ed --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALU.hpp @@ -0,0 +1,918 @@ +#ifndef _RAMTOCOSTRAINTSYSTEM_ALU_HPP_ +#define _RAMTOCOSTRAINTSYSTEM_ALU_HPP_ +#include "generalPurpose.hpp" +#include +#include +#include "TinyRAM/TinyRAMInstance.hpp" + + +using namespace gadgetlib; + + + +extern UnpackedWord unpackedArg1_; +extern UnpackedWord unpackedArg2_; +extern UnpackedWord opcodeResult_; +extern UnpackedWord opcodeCarry_; +extern UnpackedWord & opcodeAux1_; +extern UnpackedWord & opcodeAux2_; +extern UnpackedWord opcodeAux3_; +extern UnpackedWord opcodeAux4_; +extern UnpackedWord opcodeAux5_; +extern UnpackedWord opcodeAux6_; +extern UnpackedWord opcodeAux7_; +extern UnpackedWord memAux1_; +extern UnpackedWord memAux2_; +extern UnpackedWord memAux3_; +extern UnpackedWord memAux4_; +extern UnpackedWord memAux5_; +extern UnpackedWord memAux6_; +extern UnpackedWord memAux7_; +extern UnpackedWord memAux8_; + +extern unsigned int prngseed; +extern bool standAlone_; +extern FElem program_output; +extern unsigned int ROMSIZE; + +/** + * The function resetALU_GadgetGlobalState() is + * a hacked added by Michael (2018-02-14), + * after finding the reason tests involving reductions from TinyRAM to Bair + * behave differently if executed alone, or after other tests executing this reduction. + * Technically, they had a big probability to fail. + * It was found the reduction has some global variables (?!?). + * As it is not clear what they role exactly is, so a better design could be made, + * the easiest hack was to add this function, and execute it before invoking any + * instance of the reduction + */ +void resetALU_GadgetGlobalState(); + + +/********************************************************/ +/**************** Class Writing Helpers *****************/ +/********************************************************/ +// A macro to disallow any non-defined constructors +// This should be used in the private: declarations for a class +#define DISALLOW_CONSTRUCTION(TypeName) \ + TypeName(); + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_Component_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_Component_Gadget : virtual public Gadget { +public: + virtual void init() = 0; + virtual void generateConstraints() = 0; +protected: + ALU_Component_Gadget(const ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results) + : Gadget(pb), inputs_(inputs), results_(results){} + + typedef ::std::shared_ptr TRParamsPtr; + const TRParamsPtr tinyRAMparams() const; + // external variables + + const ALUInput inputs_; + const ALUOutput results_; +private: + DISALLOW_COPY_AND_ASSIGN(ALU_Component_Gadget); +}; // class ALU_Component_Gadget + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_Gadget : public Gadget { +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputVariables, + const ALUOutput& resultVariables); + void generateConstraints(); + void generateWitness(unsigned int i); + void setProgram(const TinyRAMProgram& program); + //void applyConstraintsToPB(); + + + +private: + ALU_Gadget(ProtoboardPtr pb, + const ALUInput& inputVariables, + const ALUOutput& resultVariables); + virtual void init(); + void createInternalComponents(); + + //internal variables + TinyRAMProgram program_; + GadgetPtr unpackArg1_g_, unpackArg2_g_; //Putting these here rather than in internal gadgets so booleanity checks will not be done separately for each pc value + // externals + const ALUInput inputVariables_; + const ALUOutput resultVariables_; + + //internal gadgets + ::std::map components_; ///< Internal gadgets for the opcodes + + DISALLOW_COPY_AND_ASSIGN(ALU_Gadget); +}; + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_XOR_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_XOR_Gadget : public ALU_Component_Gadget{ +private: + ALU_XOR_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); + + +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + DISALLOW_COPY_AND_ASSIGN(ALU_XOR_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_AND_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_AND_Gadget : public ALU_Component_Gadget{ +private: + ALU_AND_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_AND_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_OR_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_OR_Gadget : public ALU_Component_Gadget{ +private: + ALU_OR_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_OR_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_NOT_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_NOT_Gadget : public ALU_Component_Gadget{ +private: + ALU_NOT_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + + DISALLOW_COPY_AND_ASSIGN(ALU_NOT_Gadget); + +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_ADD_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_ADD_Gadget : public ALU_Component_Gadget { +private: + ALU_ADD_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + // internal gadgets + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + GadgetPtr packResult_g_; + GadgetPtr add_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_ADD_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SUB_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_SUB_Gadget : public ALU_Component_Gadget { +private: + ALU_SUB_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + // internal gadgets + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + GadgetPtr packResult_g_; + GadgetPtr add_g_; + DISALLOW_COPY_AND_ASSIGN(ALU_SUB_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_MULL_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_MULL_Gadget : public ALU_Component_Gadget { +private: + ALU_MULL_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + Algebra::UnpackedWord multPartials1_; + Algebra::UnpackedWord multPartials2_; + Algebra::UnpackedWord unpackedResult_; + Algebra::UnpackedWord witnessHighBits_; + Algebra::UnpackedWord dmultPartials1_; + Algebra::UnpackedWord dmultPartials2_; + // internal gadgets + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + GadgetPtr unpackResult_g_; + GadgetPtr mult_g_; + GadgetPtr dmultPack_g_; + GadgetPtr packHighBits_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_MULL_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_UMULH_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_UMULH_Gadget : public ALU_Component_Gadget { +private: + ALU_UMULH_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + Algebra::UnpackedWord multPartials1_; + Algebra::UnpackedWord multPartials2_; + Algebra::UnpackedWord unpackedResult_; + Algebra::UnpackedWord witnessLowBits_; + Algebra::UnpackedWord dmultPartials1_; + Algebra::UnpackedWord dmultPartials2_; + // internal gadgets + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + GadgetPtr unpackResult_g_; + GadgetPtr mult_g_; + GadgetPtr dmultPack_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_UMULH_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SMULH_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_SMULH_Gadget : public ALU_Component_Gadget { +private: + ALU_SMULH_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + Algebra::UnpackedWord multPartials1_; + Algebra::UnpackedWord multPartials2_; + Algebra::UnpackedWord unpackedResult_; + Algebra::UnpackedWord witnessLowBits_; + Algebra::UnpackedWord dmultPartials1_; + Algebra::UnpackedWord dmultPartials2_; + // internal gadgets + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + GadgetPtr unpackResult_g_; + GadgetPtr mult_g_; + GadgetPtr dmultPack_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_SMULH_Gadget); +}; + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_UDIV_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_UDIV_Gadget : public ALU_Component_Gadget { +private: + ALU_UDIV_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + Algebra::UnpackedWord cmpFlags_; + Algebra::Variable isGEQ_; + Algebra::UnpackedWord unpackedResult_; + Algebra::UnpackedWord multPartials1_; + Algebra::UnpackedWord multPartials2_; + Algebra::UnpackedWord mpartialsArg1_; + Algebra::Variable mpackArg1_; + Algebra::UnpackedWord mpartialsRemainder_; + Algebra::Variable mpackRemainder_; + Algebra::UnpackedWord witnessRemainder_; + // internal gadgets + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + GadgetPtr unpackResult_g_; + GadgetPtr mult_g_; + GadgetPtr multPackArg1_g_; + GadgetPtr multPackRemainder_g_; + GadgetPtr GEQ_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_UDIV_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_UMOD_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_UMOD_Gadget : public ALU_Component_Gadget { +private: + ALU_UMOD_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + Algebra::UnpackedWord cmpFlags_; + Algebra::Variable isGEQ_; + Algebra::UnpackedWord multPartials1_; + Algebra::UnpackedWord multPartials2_; + Algebra::UnpackedWord mpartialsArg1_; + Algebra::Variable mpackArg1_; + Algebra::UnpackedWord mpartialsRemainder_; + Algebra::Variable mpackRemainder_; + Algebra::UnpackedWord witnessRemainder_; + Algebra::UnpackedWord witnessResult_; + // internal gadgets + GadgetPtr unpackArg1_g_; + GadgetPtr unpackArg2_g_; + GadgetPtr packResult_g_; + GadgetPtr mult_g_; + GadgetPtr multPackArg1_g_; + GadgetPtr multPackRemainder_g_; + GadgetPtr GEQ_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_UMOD_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPE_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_CMPE_Gadget : public ALU_Component_Gadget { +private: + ALU_CMPE_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + +private: + Algebra::Variable pInverse; + DISALLOW_COPY_AND_ASSIGN(ALU_CMPE_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPA_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_CMPA_Gadget : public ALU_Component_Gadget { +private: + ALU_CMPA_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + Algebra::UnpackedWord cmpFlags_; + Algebra::Variable isGEQ_; + GadgetPtr unpack1_g_; + GadgetPtr unpack2_g_; + GadgetPtr compareArgs_; + DISALLOW_COPY_AND_ASSIGN(ALU_CMPA_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPAE_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_CMPAE_Gadget : public ALU_Component_Gadget { +private: + ALU_CMPAE_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + Algebra::UnpackedWord cmpFlags_; + Algebra::Variable isGEQ_; + GadgetPtr unpack1_g_; + GadgetPtr unpack2_g_; + GadgetPtr compareArgs_; + DISALLOW_COPY_AND_ASSIGN(ALU_CMPAE_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPG_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_CMPG_Gadget : public ALU_Component_Gadget { +private: + ALU_CMPG_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + Algebra::UnpackedWord cmpFlags_; + Algebra::Variable isGEQ_; + GadgetPtr unpack1_g_; + GadgetPtr unpack2_g_; + GadgetPtr compareArgs_; + DISALLOW_COPY_AND_ASSIGN(ALU_CMPG_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CMPGE_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_CMPGE_Gadget : public ALU_Component_Gadget { +private: + ALU_CMPGE_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + Algebra::UnpackedWord cmpFlags_; + Algebra::Variable isGEQ_; + GadgetPtr unpack1_g_; + GadgetPtr unpack2_g_; + GadgetPtr compareArgs_; + DISALLOW_COPY_AND_ASSIGN(ALU_CMPGE_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SHL_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_SHL_Gadget : public ALU_Component_Gadget { +private: + ALU_SHL_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + enum idxs {dRes=1, rem=2, invRem=3, witnessFlag=4}; + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + Algebra::UnpackedWord auxArr_; + // internal gadgets + GadgetPtr unpackDouble_g_; + GadgetPtr packResult_g_; + + DISALLOW_COPY_AND_ASSIGN(ALU_SHL_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_SHR_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_SHR_Gadget : public ALU_Component_Gadget { +private: + ALU_SHR_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + enum idxs {dRes=1, rem=2, invRem=3, witnessFlag=4}; + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + Algebra::UnpackedWord auxArr_; + // internal gadgets + GadgetPtr unpackDouble_g_; + GadgetPtr packResult_g_; + DISALLOW_COPY_AND_ASSIGN(ALU_SHR_Gadget); +}; + +/*********************************/ +/*** END OF Gadget ***/ +/*********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_JMP_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_JMP_Gadget : public ALU_Component_Gadget { +private: + ALU_JMP_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + DISALLOW_COPY_AND_ASSIGN(ALU_JMP_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CJMP_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_CJMP_Gadget : public ALU_Component_Gadget{ +private: + ALU_CJMP_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); + +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + + DISALLOW_COPY_AND_ASSIGN(ALU_CJMP_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_CNJMP_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_CNJMP_Gadget : public ALU_Component_Gadget{ +private: + ALU_CNJMP_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + + DISALLOW_COPY_AND_ASSIGN(ALU_CNJMP_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_STOEW_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_STOREW_Gadget : public ALU_Component_Gadget { +private: + ALU_STOREW_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + + DISALLOW_COPY_AND_ASSIGN(ALU_STOREW_Gadget); +}; + +/*********************************/ +/*** END OF Gadget ***/ +/*********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_LOADW_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ +class ALU_LOADW_Gadget : public ALU_Component_Gadget { +private: + ALU_LOADW_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + + DISALLOW_COPY_AND_ASSIGN(ALU_LOADW_Gadget); +}; + +/*********************************/ +/*** END OF Gadget ***/ +/*********************************/ + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_XOR_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_ANSWER_Gadget : public ALU_Component_Gadget{ +private: + ALU_ANSWER_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + + DISALLOW_COPY_AND_ASSIGN(ALU_ANSWER_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_MOV_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_MOV_Gadget : public ALU_Component_Gadget{ +private: + ALU_MOV_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); + +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); + + DISALLOW_COPY_AND_ASSIGN(ALU_MOV_Gadget); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALU_RESERVED_OPCODE_24_Gadget ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +class ALU_RESERVED_OPCODE_24_Gadget : public ALU_Component_Gadget { +private: + ALU_RESERVED_OPCODE_24_Gadget(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + virtual void init(); +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUInput& inputs, + const ALUOutput& results); + void generateConstraints(); + void generateWitness(); +private: + GadgetPtr unpackArg1_g_; + DISALLOW_COPY_AND_ASSIGN(ALU_RESERVED_OPCODE_24_Gadget); +}; + + + + + +#endif //_RAMTOCOSTRAINTSYSTEM_ALU_HPP_ diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALUInputConsistency.cpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALUInputConsistency.cpp new file mode 100644 index 0000000..152ae97 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALUInputConsistency.cpp @@ -0,0 +1,86 @@ +#include "ALUInputConsistency.hpp" +#include +#include + + + +ALUInputConsistency::ALUInputConsistency(ProtoboardPtr pb, + const TraceVariables& input, + const ALUInput& output) : + Gadget(pb), input_(input), output_(output), + program_("program", + std::dynamic_pointer_cast(pb_->params())->numRegisters(), + std::dynamic_pointer_cast(pb_->params())->registerLength()){} + + +void ALUInputConsistency::init(){}; + +GadgetPtr ALUInputConsistency::create(ProtoboardPtr pb, + const TraceVariables& input, + const ALUInput& output){ + GadgetPtr pGadget(new ALUInputConsistency(pb,input,output)); + pGadget->init(); + return pGadget; +} + +void ALUInputConsistency::setProgram(const TinyRAMProgram& program){ + program_ = program; +} + + +void ALUInputConsistency::generateConstraints(){ + GADGETLIB_ASSERT(program_.size() > 0, "ALUInputConsistency: The program should be initialized"); + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + vector arg1Polynomials, arg2Polynomials, destPolynomials; + CircuitPolynomial resArg1, resArg2, resDest; + vector selectorToConstraint; + vector selectorRelevant; + + for (unsigned int i = 0; i < program_.size(); ++i){ + unsigned int arg1 = program_.code()[i].arg1Idx_; + unsigned int dest = program_.code()[i].destIdx_; + bool arg2IsImmediate = program_.code()[i].arg2isImmediate_; //If 1 then arg2 is immediate + CircuitPolynomial arg2Poly; + if (!arg2IsImmediate){ + unsigned int arg2 = program_.code()[i].arg2IdxOrImmediate_; + arg2Poly = input_.registers_[arg2] + output_.arg2_val_; + } + else{ //Arg2 is immediate + Algebra::FElem valArg2 = mapIntegerToFieldElement(0, params->registerLength(), program_.code()[i].arg2IdxOrImmediate_); + arg2Poly = valArg2 + output_.arg2_val_; + } + CircuitPolynomial arg1Poly(input_.registers_[arg1] + output_.arg1_val_); + CircuitPolynomial destPoly(input_.registers_[dest] + output_.dest_val_); + arg1Polynomials.emplace_back(arg1Poly); + arg2Polynomials.emplace_back(arg2Poly); + destPolynomials.emplace_back(destPoly); + selectorToConstraint.emplace_back(i); + selectorRelevant.push_back(true); + } + vector opcodeVars = getPCVars(input_.pc_); + CircuitPolynomial SArg1(SelectorSum(arg1Polynomials, opcodeVars, selectorToConstraint, selectorRelevant)); + CircuitPolynomial SArg2(SelectorSum(arg2Polynomials, opcodeVars, selectorToConstraint, selectorRelevant)); + CircuitPolynomial SDest(SelectorSum(destPolynomials, opcodeVars, selectorToConstraint, selectorRelevant)); + pb_->addGeneralConstraint(SArg1, "SelectorSum_ARG1", Opcode::NONE); + pb_->addGeneralConstraint(SArg2, "SelectorSum_ARG2", Opcode::NONE); + pb_->addGeneralConstraint(SDest, "SelectorSum_Dest", Opcode::NONE); +}; + +void ALUInputConsistency::generateWitness(unsigned int i){ + GADGETLIB_ASSERT(i < program_.size(), "ALUInputConsistency: in order to generate witness i should be less the the program size"); + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + unsigned int arg1 = program_.code()[i].arg1Idx_; + unsigned int dest = program_.code()[i].destIdx_; + bool arg2IsImmidiate = program_.code()[i].arg2isImmediate_; //If 1 then arg2 is immidiate + if (!arg2IsImmidiate){ + unsigned int arg2 = program_.code()[i].arg2IdxOrImmediate_; + pb_->val(output_.arg2_val_) = pb_->val(input_.registers_[arg2]); + } + else{ + pb_->val(output_.arg2_val_) = mapIntegerToFieldElement(0, params->registerLength(), program_.code()[i].arg2IdxOrImmediate_); + } + pb_->val(output_.arg1_val_) = pb_->val(input_.registers_[arg1]); + pb_->val(output_.dest_val_) = pb_->val(input_.registers_[dest]); +}; + + diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALUInputConsistency.hpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALUInputConsistency.hpp new file mode 100644 index 0000000..fd936de --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/ALUInputConsistency.hpp @@ -0,0 +1,37 @@ +#ifndef ALU_INPUT_CONSISTENCY_HPP +#define ALU_INPUT_CONSISTENCY_HPP + +#include +#include +#include "generalPurpose.hpp" +#include "TinyRAM/TinyRAMInstance.hpp" + +using namespace gadgetlib; + + + +class ALUInputConsistency : public Gadget{ +private: + TraceVariables input_; + ALUInput output_; + TinyRAMProgram program_; + + ALUInputConsistency(ProtoboardPtr pb, + const TraceVariables& input, + const ALUInput& output); + virtual void init(); + + +public: + static GadgetPtr create(ProtoboardPtr pb, + const TraceVariables& input, + const ALUInput& output); + void setProgram(const TinyRAMProgram& program); + void generateConstraints(); + void generateWitness(unsigned int i); + +}; + + + +#endif //ALU_INPUT_CONSISTENCY_HPP diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/MemoryConsraints.cpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/MemoryConsraints.cpp new file mode 100644 index 0000000..6c3a4b7 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/MemoryConsraints.cpp @@ -0,0 +1,72 @@ +#include "MemoryConsraints.hpp" +#include +#include "TinyRAMtoBair/RamToContraintSystem/ALU.hpp" + + + + MemoryConstraints::MemoryConstraints(ProtoboardPtr pb, + const MemoryFollowingTraceVariables& followingTraceVariables) : + Gadget(pb), + unpackedaddr1_(unpackedArg1_), + unpackedaddr2_(unpackedArg2_), + unpackedTimestamp1_(opcodeAux1_), + unpackedTimestamp2_(opcodeAux2_), + addressFlags_(opcodeAux3_), + timestampFlags_(opcodeAux4_), + addressIsGEQ_(opcodeAux3_[0]), + timestampIsGEQ_(opcodeAux4_[0]), + multPartials1_(opcodeAux5_), + multPartials2_(opcodeAux6_), + first_(followingTraceVariables.first_), + second_(followingTraceVariables.second_){}; + +GadgetPtr MemoryConstraints::create(ProtoboardPtr pb, + const MemoryFollowingTraceVariables& followingTraceVariables){ + GadgetPtr pGadget(new MemoryConstraints(pb, followingTraceVariables)); + pGadget->init(); + return pGadget; +} + +void MemoryConstraints::init(){ + unpackAddr1_g_ = CompressionPacking_Gadget::create(pb_, unpackedaddr1_, first_.address_, PackingMode::UNPACK ,Opcode::MEMORY); + unpackAddr2_g_ = CompressionPacking_Gadget::create(pb_, unpackedaddr2_, second_.address_, PackingMode::UNPACK ,Opcode::MEMORY); + //Checks if addr1 >= addr2 + compareAddresses_ = GreaterEqual_Gadget::create(pb_, unpackedaddr2_, unpackedaddr1_, addressFlags_, addressIsGEQ_, false, Opcode::MEMORY); + unpackTimestamp1_g_ = MultiplicationPacking_Gadget::create(pb_, unpackedTimestamp1_, multPartials1_, first_.timeStamp_, false, PackingMode::UNPACK, Opcode::MEMORY); + unpackTimestamp2_g_ = MultiplicationPacking_Gadget::create(pb_, unpackedTimestamp2_, multPartials2_, second_.timeStamp_, false, PackingMode::UNPACK, Opcode::MEMORY); + compareTimestamps_g_ = GreaterEqual_Gadget::create(pb_, unpackedTimestamp2_, unpackedTimestamp1_, timestampFlags_, timestampIsGEQ_, false, Opcode::MEMORY); +} + +void MemoryConstraints::generateConstraints(){ + //std::cout << "csMEM: " << pb_->constraintSystem(Opcode::MEMORY).getNumberOfConstraints() << std::endl; + GADGETLIB_ASSERT(0 == pb_->constraintSystem(Opcode::MEMORY).getNumberOfConstraints(), "op::MEMORY cs is clear"); + unpackAddr1_g_->generateConstraints(); + unpackAddr2_g_->generateConstraints(); + compareAddresses_->generateConstraints(); + unpackTimestamp1_g_->generateConstraints(); + unpackTimestamp2_g_->generateConstraints(); + compareTimestamps_g_->generateConstraints(); + auto cons = pb_->constraintSystem(Opcode::MEMORY).getConstraints(); + pb_->clearConstraintSystem(Opcode::MEMORY); + for (Constraint constraint : cons){ + addGeneralConstraint(second_.isMemOp_ * constraint.constraint(), "isMemOp*isMemOp' * " + constraint.asString(), Opcode::MEMORY); + } + const Algebra::FElem g = Algebra::xFE(); + pb_->addMemoryConstraint((first_.isMemOp_ + Algebra::one()) * second_.isMemOp_, "(1 - isMemOp)* isMemOp'"); + pb_->addMemoryConstraint(second_.isMemOp_ * (g + addressIsGEQ_) * (Algebra::one() + addressIsGEQ_),"isMemOp * isMemOp' * (1 + addrflagEqual) * (1 + addrflagGreater)"); + pb_->addMemoryConstraint(second_.isMemOp_ * addressIsGEQ_ * (Algebra::one() + addressIsGEQ_) * second_.isLoad_ * (first_.value_ + second_.value_), "isMemOp* isMemOp' *addrflagEqual*isLoad' *(value - value') = 0"); + pb_->addMemoryConstraint(second_.isMemOp_ * addressIsGEQ_ * (Algebra::one() + addressIsGEQ_) * (Algebra::one() + timestampIsGEQ_), "isMemOp * isMemOp' * addrflagEqual *(1 + timestampflagGreater)"); +} + +void MemoryConstraints::generateWitness(){ + unpackAddr1_g_->generateWitness(); + unpackAddr2_g_->generateWitness(); + compareAddresses_->generateWitness(); + unsigned int degree1 = pb_->getDegreeOfFElem(pb_->val(first_.timeStamp_)); + unsigned int degree2 = pb_->getDegreeOfFElem(pb_->val(second_.timeStamp_)); + std::dynamic_pointer_cast(unpackTimestamp1_g_)->generateWitness(degree1); + std::dynamic_pointer_cast(unpackTimestamp2_g_)->generateWitness(degree2); + compareTimestamps_g_->generateWitness(); +} + + diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/MemoryConsraints.hpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/MemoryConsraints.hpp new file mode 100644 index 0000000..e9e3349 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/MemoryConsraints.hpp @@ -0,0 +1,47 @@ +#ifndef _COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_MEMORY_CONSTRAINTS_HPP_ +#define _COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_MEMORY_CONSTRAINTS_HPP_ + +#include +#include +#include "TinyRAMtoBair/RamToContraintSystem/generalPurpose.hpp" + + + +class MemoryConstraints : public Gadget{ +private: + MemoryConstraints(ProtoboardPtr pb, + const MemoryFollowingTraceVariables& followingTraceVariables); + + void init(); + + Algebra::UnpackedWord unpackedaddr1_; + Algebra::UnpackedWord unpackedaddr2_; + Algebra::UnpackedWord unpackedTimestamp1_; + Algebra::UnpackedWord unpackedTimestamp2_; + Algebra::UnpackedWord addressFlags_; + Algebra::UnpackedWord timestampFlags_; + Algebra::Variable addressIsGEQ_; + Algebra::Variable timestampIsGEQ_; + Algebra::UnpackedWord multPartials1_; + Algebra::UnpackedWord multPartials2_; + + GadgetPtr unpackAddr1_g_; + GadgetPtr unpackAddr2_g_; + GadgetPtr compareAddresses_; + GadgetPtr unpackTimestamp1_g_; + GadgetPtr unpackTimestamp2_g_; + GadgetPtr compareTimestamps_g_; + + MemoryTraceVariables first_; + MemoryTraceVariables second_; +public: + static GadgetPtr create(ProtoboardPtr pb, + const MemoryFollowingTraceVariables& followingTraceVariables); + + void generateConstraints(); + void generateWitness(); + +}; + + +#endif //_COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_MEMORY_CONSTRAINTS_HPP_ diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/generalPurpose.cpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/generalPurpose.cpp new file mode 100644 index 0000000..b2841fc --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/generalPurpose.cpp @@ -0,0 +1,116 @@ +#include "generalPurpose.hpp" + + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* TraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +TraceVariables::TraceVariables(unsigned int pcLength, unsigned int numberOfRegisters) : + timeStamp_("timeStamp"), flag_("flag"), pc_(pcLength, "pc"), registers_(numberOfRegisters, "register"){} + +TraceVariables::TraceVariables(const Algebra::Variable timeStamp,const Algebra::FlagVariable flag, + const Algebra::UnpackedWord pc,const Algebra::VariableArray registers) : + timeStamp_(timeStamp), flag_(flag), pc_(pc), registers_(registers){} + +int TraceVariables::size() const { return 2 + pc_.size() + registers_.size(); } +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* FollowingTraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +FollowingTraceVariables::FollowingTraceVariables(unsigned int pcLength, unsigned int numberOfRegisters) : + first_(pcLength, numberOfRegisters), second_(pcLength, numberOfRegisters){} + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALUInput ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + + +ALUInput::ALUInput(const Algebra::Variable& arg1_val, const Algebra::Variable& arg2_val, + const Algebra::Variable& dest_val, const Algebra::FlagVariable& flag, const Algebra::UnpackedWord& pc) : + arg1_val_(arg1_val), arg2_val_(arg2_val), dest_val_(dest_val), flag_(flag),pc_(pc){}; + + +ALUInput::ALUInput() : arg1_val_("ALU arg1_val"), arg2_val_("ALU arg2_val"), dest_val_("ALU dest_val"), + flag_("ALU flag"),pc_(){} + +ALUInput::ALUInput(const Algebra::FlagVariable& flag, const Algebra::UnpackedWord& pc) : + arg1_val_("ALU arg1_val"), arg2_val_("ALU arg2_val"), dest_val_("ALU dest_val"), + flag_(flag),pc_(pc){} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALUOutput ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +ALUOutput::ALUOutput(const Algebra::Variable& flag, const Algebra::Variable& result, + const Algebra::FlagVariable& isMemOp, const Algebra::Variable& address, + const Algebra::FlagVariable& isLoadOp,const Algebra::Variable& value) : + flag_(flag), result_(result),isMemOp_(isMemOp),address_(address), isLoadOp_(isLoadOp),value_(value){}; + +ALUOutput::ALUOutput(const Algebra::Variable& flag) : flag_(flag), result_("ALU result"), isMemOp_("isMemOp"), + address_("ALU adress"),isLoadOp_("isLoadOp"),value_("address value"){} + +ALUOutput::ALUOutput(const Algebra::Variable& flag, const Algebra::FlagVariable& isMemOp, const Algebra::Variable& address, + const Algebra::FlagVariable& isLoadOp, const Algebra::Variable& value) : + flag_(flag), result_("ALU result"), isMemOp_(isMemOp), address_(address), isLoadOp_(isLoadOp), value_(value){}; + +ALUOutput::ALUOutput() : flag_("ALU flag"), result_("ALU result"), isMemOp_("isMemOP"), address_("ALU adress"), + isLoadOp_("isLoadOp"), value_("address value_"){} + + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* MemoryTraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +MemoryTraceVariables::MemoryTraceVariables(const Algebra::Variable& isMemOp, + const Algebra::Variable& address, + const Algebra::Variable& value, + const Algebra::FlagVariable& isLoad, + const Algebra::Variable& timeStamp) : + isMemOp_(isMemOp),address_(address), value_(value), isLoad_(isLoad), timeStamp_(timeStamp){} + +MemoryTraceVariables::MemoryTraceVariables(const Algebra::Variable& timeStamp) : isMemOp_("Memory isMemOp"), + address_("Memory Address"), value_("Memory Value"), isLoad_("Memory isLoad"), timeStamp_(timeStamp){} + +MemoryTraceVariables::MemoryTraceVariables() :isMemOp_("Memory isMemOp"), address_("Memory Address"), value_("Memory Value"), + isLoad_("Memory isLoad"), timeStamp_("Memory timeStamp"){} + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* MemoryFollowingTraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +MemoryFollowingTraceVariables::MemoryFollowingTraceVariables() : first_(), second_(){} + +MemoryFollowingTraceVariables::MemoryFollowingTraceVariables(const Algebra::Variable& timeStamp1, + const Algebra::Variable& timeStamp2) : + first_(timeStamp1), second_(timeStamp2){} + + + + + diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/generalPurpose.hpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/generalPurpose.hpp new file mode 100644 index 0000000..f15a9a1 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/generalPurpose.hpp @@ -0,0 +1,154 @@ +#ifndef CONSTARINTSLIB2_GENERAL_PURPOSE_HPP_ +#define CONSTARINTSLIB2_GENERAL_PURPOSE_HPP_ + +#include +#include +#include + +using namespace Algebra; +using namespace gadgetlib; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* TraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +struct TraceVariables{ + Algebra::Variable timeStamp_; + Algebra::FlagVariable flag_; + Algebra::UnpackedWord pc_; + Algebra::VariableArray registers_; + + TraceVariables(const Algebra::Variable timeStamp, + const Algebra::FlagVariable flag, + const Algebra::UnpackedWord pc, + const Algebra::VariableArray registers); + + TraceVariables(unsigned int pcLength, unsigned int numberOfRegisters); + + int size() const; +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* FollowingTraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +struct FollowingTraceVariables{ + TraceVariables first_; + TraceVariables second_; + + FollowingTraceVariables(unsigned int pcLength, unsigned int numberOfRegisters); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALUInput ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + + +struct ALUInput{ + Algebra::Variable arg1_val_; + Algebra::Variable arg2_val_; + Algebra::Variable dest_val_; + Algebra::FlagVariable flag_; + Algebra::UnpackedWord pc_; + + ALUInput(const Algebra::Variable& arg1_val, + const Algebra::Variable& arg2_val, + const Algebra::Variable& dest_val, + const Algebra::FlagVariable& flag, + const Algebra::UnpackedWord& pc); + + ALUInput(const Algebra::FlagVariable& flag, + const Algebra::UnpackedWord& pc); + + ALUInput(); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* ALUOutput ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +struct ALUOutput{ + Algebra::Variable flag_; + Algebra::Variable result_; + Algebra::FlagVariable isMemOp_; + Algebra::Variable address_; + Algebra::FlagVariable isLoadOp_; + Algebra::Variable value_; + + ALUOutput(const Algebra::Variable& flag, + const Algebra::Variable& result, + const Algebra::FlagVariable& isMemOp, + const Algebra::Variable& address, + const Algebra::FlagVariable& isLoadOp, + const Algebra::Variable& value); + + ALUOutput(const Algebra::Variable& flag, + const Algebra::FlagVariable& isMemOp, + const Algebra::Variable& address, + const Algebra::FlagVariable& isLoadOp, + const Algebra::Variable& value); + + ALUOutput(const Algebra::Variable& flag); + + ALUOutput(); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* MemoryTraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +struct MemoryTraceVariables{ + Algebra::Variable isMemOp_; + Algebra::Variable address_; + Algebra::Variable value_; + Algebra::FlagVariable isLoad_; + Algebra::Variable timeStamp_; + + MemoryTraceVariables(const Algebra::Variable& isMemOp, + const Algebra::Variable& address, + const Algebra::Variable& value, + const Algebra::FlagVariable& isLoad, + const Algebra::Variable& timeStamp); + + MemoryTraceVariables(const Algebra::Variable& timeStamp); + MemoryTraceVariables(); +}; + +/*************************************************************************************************/ +/*************************************************************************************************/ +/******************* ******************/ +/******************* MemoryFollowingTraceVariables ******************/ +/******************* ******************/ +/*************************************************************************************************/ +/*************************************************************************************************/ + +struct MemoryFollowingTraceVariables{ + MemoryTraceVariables first_; + MemoryTraceVariables second_; + + MemoryFollowingTraceVariables(); + MemoryFollowingTraceVariables(const Algebra::Variable& timeStamp1, + const Algebra::Variable& timeStamp2); +}; + +#endif diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/traceConsistency.cpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/traceConsistency.cpp new file mode 100644 index 0000000..2940530 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/traceConsistency.cpp @@ -0,0 +1,401 @@ +#include +#include "traceConsistency.hpp" + + + + + TraceConsistency::TraceConsistency(ProtoboardPtr pb, + const ALUOutput& aluOutput, + const FollowingTraceVariables& followingTraceVariables) : + Gadget(pb), aluOutput_(aluOutput), followingTraceVariables_(followingTraceVariables), + program_("program", + std::dynamic_pointer_cast(pb_->params())->numRegisters(), + std::dynamic_pointer_cast(pb_->params())->registerLength()){} + + + void TraceConsistency::timeStampConsistency(){ + const Algebra::FElem generator = Algebra::FElem(getGF2E_X()); + // timestamp constraint + Algebra::Variable timeStamp0 = followingTraceVariables_.first_.timeStamp_; + Algebra::Variable timeStamp1 = followingTraceVariables_.second_.timeStamp_; + CircuitPolynomial timeStampPoly(timeStamp1 + timeStamp0 * generator); + pb_->addGeneralConstraint(timeStampPoly, "timestamp1 + timestamp0 * x", Opcode::NONE); + } + + void TraceConsistency::pcConsistency(){ + const Algebra::FElem generator = Algebra::FElem(getGF2E_X()); + const int pcLength = program_.pcLength(); + Algebra::FElem x_i = Algebra::one(); + //// Calculate pc as packed sum_{i=0}^{i=registerLength} pc[i] * x_i + //CircuitPolynomial pcPacked(followingTraceVariables_.second_.pc_[0] * x_i); + //CircuitPolynomial prevPcPacked(followingTraceVariables_.first_.pc_[0] * x_i); + // + //for (int i = 1; i < pcLength; i++){ + // x_i *= generator; + // pcPacked = pcPacked + (followingTraceVariables_.second_.pc_[i] * x_i); + // prevPcPacked = prevPcPacked + (followingTraceVariables_.first_.pc_[i] * x_i); + //} + + + //CircuitPolynomial pcPoly; + //for (int i = 0; i < program_.size(); i++){ + // CircuitPolynomial selector_i = getSelector(program_.size(), i, followingTraceVariables_.first_.pc_); + // Opcode opcode = program_.code()[i].opcode_; + // if (opcode == Opcode::JMP){ + // pcPoly = pcPoly + ((pcPacked + aluOutput_.result_) * selector_i); + // } + // else if (opcode == Opcode::ANSWER){ + // pcPoly = pcPoly + ((pcPacked + prevPcPacked) * selector_i); + // } + // else if (opcode == Opcode::CJMP){ + // Algebra::FElem integerAsFElem = mapIntegerToFieldElement(0, pcLength, i + 1); + // Variable flag = followingTraceVariables_.first_.flag_; + // pcPoly = pcPoly + selector_i*(flag * (pcPacked + aluOutput_.result_) + + // (Algebra::one() + flag) * (pcPacked + integerAsFElem)); + // } + // else if (opcode == Opcode::CNJMP){ + // Algebra::FElem integerAsFElem = mapIntegerToFieldElement(0, pcLength, i + 1); + // Variable flag = followingTraceVariables_.first_.flag_; + // pcPoly = pcPoly + selector_i*((Algebra::one() + flag) * (pcPacked + aluOutput_.result_) + + // flag * (pcPacked + integerAsFElem)); + // } + // else{ + // Algebra::FElem integerAsFElem = mapIntegerToFieldElement(0, pcLength, i + 1); + // pcPoly = pcPoly + ((pcPacked + integerAsFElem) *selector_i); + // } + + //} + + + //******************* + // Calculate pc as packed sum_{i=0}^{i=registerLength} pc[i] * x_i + LinearCombination pcPacked(followingTraceVariables_.second_.pc_[0] * x_i); + LinearCombination prevPcPacked(followingTraceVariables_.first_.pc_[0] * x_i); + + + for (int i = 1; i < pcLength; i++){ + x_i *= generator; + pcPacked = pcPacked + (followingTraceVariables_.second_.pc_[i] * x_i); + prevPcPacked = prevPcPacked + (followingTraceVariables_.first_.pc_[i] * x_i); + } + //CircuitPolynomial pcPacked(pcPackedLC); + //CircuitPolynomial prevPcPacked(prevPcPackedLC); + + vector selectorToConstraint(program_.size()); + vector selectorVariableVector = getPCVars(followingTraceVariables_.first_.pc_); + //the vector that will contain the different relevant constraints according to the opcode + vector constraints; + constraints.push_back(pcPacked + aluOutput_.result_); + constraints.push_back(pcPacked + prevPcPacked); + CircuitPolynomial pcPoly; + for (unsigned int i = 0; i < program_.size(); i++){ + CircuitPolynomial selector_i = getSelector(program_.size(), i, followingTraceVariables_.first_.pc_); + Opcode opcode = program_.code()[i].opcode_; + if (opcode == Opcode::JMP){ + //pcPoly = pcPoly + ((pcPacked + aluOutput_.result_) * selector_i); + selectorToConstraint[i] = 0; + } + else if (opcode == Opcode::ANSWER){ + //pcPoly = pcPoly + ((pcPacked + prevPcPacked) * selector_i); + selectorToConstraint[i] = 1; + } + else if (opcode == Opcode::CJMP){ + Algebra::FElem integerAsFElem = mapIntegerToFieldElement(0, pcLength, i + 1); + Variable flag = followingTraceVariables_.first_.flag_; + //pcPoly = pcPoly + selector_i*(flag * (pcPacked + aluOutput_.result_) + + // (Algebra::one() + flag) * (pcPacked + integerAsFElem)); + constraints.push_back((flag * (pcPacked + aluOutput_.result_) + + (Algebra::one() + flag) * (pcPacked + integerAsFElem))); + selectorToConstraint[i] = constraints.size() - 1; + + } + else if (opcode == Opcode::CNJMP){ + Algebra::FElem integerAsFElem = mapIntegerToFieldElement(0, pcLength, i + 1); + Variable flag = followingTraceVariables_.first_.flag_; + //pcPoly = pcPoly + selector_i*((Algebra::one() + flag) * (pcPacked + aluOutput_.result_) + + // flag * (pcPacked + integerAsFElem)); + constraints.push_back((Algebra::one() + flag) * (pcPacked + aluOutput_.result_) + + flag * (pcPacked + integerAsFElem)); + selectorToConstraint[i] = constraints.size() - 1; + + } + else{ + Algebra::FElem integerAsFElem = mapIntegerToFieldElement(0, pcLength, i + 1); + //pcPoly = pcPoly + ((pcPacked + integerAsFElem) *selector_i); + constraints.push_back((pcPacked + integerAsFElem)); + selectorToConstraint[i] = constraints.size() - 1; + } + + } + + + SelectorSum constraintPoly(constraints, selectorVariableVector, selectorToConstraint); + pb_->addGeneralConstraint(constraintPoly, "sum_{i=0}^{programLength} (selector(i) * (pcAsFElem + (i+1)))", Opcode::NONE); + } + + void TraceConsistency::registerConsistency(){ + unsigned int numRegistersTrace1 = followingTraceVariables_.first_.registers_.size(); + unsigned int numRegistersTrace2 = followingTraceVariables_.second_.registers_.size(); + GADGETLIB_ASSERT(numRegistersTrace1 == numRegistersTrace2, "TraceConsistency: number of registers should be the same"); + + + + for (unsigned int i = 0; i < numRegistersTrace1; ++i){ + Variable regiSecond = followingTraceVariables_.second_.registers_[i]; + Variable regiFirst = followingTraceVariables_.first_.registers_[i]; + + vector selectorToConstraint(program_.size()); + vector selectorVariableVector = getPCVars(followingTraceVariables_.first_.pc_); + //preparing the vector that will contain the different relevant constraints according to the opcode + vector constraints; + + + constraints.push_back(regiSecond + aluOutput_.result_); + constraints.push_back(regiSecond + regiFirst); + constraints.push_back(regiSecond + aluOutput_.value_); + + for (unsigned int j = 0; j < program_.size(); j++){ + Opcode opcode = program_.code()[j].opcode_; + unsigned int dest = program_.code()[j].destIdx_; + if (dest == i){ + switch (opcode) + { + case Opcode::ADD: + case Opcode::SUB: + case Opcode::AND: + case Opcode::OR: + case Opcode::XOR: + case Opcode::MOV: + case Opcode::SHL: + case Opcode::SHR: + case Opcode::RESERVED_OPCODE_24: + selectorToConstraint[j] = 0; + //constraintPoly = constraintPoly + (selector_j * (regiSecond + aluOutput_.result_)); + break; + case Opcode::JMP: + case Opcode::CJMP: + case Opcode::CNJMP: + case Opcode::CMPE: + case Opcode::CMPA: + case Opcode::CMPAE: + case Opcode::CMPG: + case Opcode::CMPGE: + case Opcode::STOREW: + case Opcode::ANSWER: + selectorToConstraint[j] = 1; + //constraintPoly = constraintPoly + (selector_j * (regiSecond + regiFirst)); + break; + case Opcode::LOADW: + selectorToConstraint[j] = 2; + //constraintPoly = constraintPoly + (selector_j * (regiSecond + aluOutput_.value_)); + break; + default: + GADGETLIB_FATAL("Trace Consistency: No Such opcode exists"); + break; + } + } + else{ + selectorToConstraint[j] = 1; + //constraintPoly = constraintPoly + (selector_j * (regiSecond + regiFirst)); + } + } + SelectorSum constraintPoly(constraints, selectorVariableVector, selectorToConstraint); + pb_->addGeneralConstraint(constraintPoly, "sum_{i=0}^{programLength} (selector(i) * (R'_j - R_j_OR_ALUres))", Opcode::NONE); + } + + //unsigned int numRegistersTrace1 = followingTraceVariables_.first_.registers_.size(); + //unsigned int numRegistersTrace2 = followingTraceVariables_.second_.registers_.size(); + //GADGETLIB_ASSERT(numRegistersTrace1 == numRegistersTrace2, "TraceConsistency: number of registers should be the same"); + //for (int i = 0; i < numRegistersTrace1; ++i){ + // CircuitPolynomial constraintPoly; + // for (int j = 0; j < program_.size(); j++){ + // CircuitPolynomial selector_j = getSelector(program_.size(), j, followingTraceVariables_.first_.pc_); + // Opcode opcode = program_.code()[j].opcode_; + // Variable regiSecond = followingTraceVariables_.second_.registers_[i]; + // Variable regiFirst = followingTraceVariables_.first_.registers_[i]; + // unsigned int dest = program_.code()[j].destIdx_; + // if (dest == i){ + // switch (opcode) + // { + // case Opcode::ADD: + // case Opcode::SUB: + // case Opcode::AND: + // case Opcode::OR: + // case Opcode::XOR: + // case Opcode::MOV: + // case Opcode::SHL: + // case Opcode::SHR: + // constraintPoly = constraintPoly + (selector_j * (regiSecond + aluOutput_.result_)); + // break; + // case Opcode::JMP: + // case Opcode::CJMP: + // case Opcode::CNJMP: + // case Opcode::CMPE: + // case Opcode::CMPA: + // case Opcode::CMPAE: + // case Opcode::CMPG: + // case Opcode::CMPGE: + // case Opcode::STOREW: + // case Opcode::ANSWER: + // constraintPoly = constraintPoly + (selector_j * (regiSecond + regiFirst)); + // break; + // case Opcode::LOADW: + // constraintPoly = constraintPoly + (selector_j * (regiSecond + aluOutput_.value_)); + // break; + // default: + // GADGETLIB_FATAL("Trace Consistency: No Such opcode exists"); + // break; + // } + // } + // else{ + // constraintPoly = constraintPoly + (selector_j * (regiSecond + regiFirst)); + // } + // } + // pb_->addGeneralConstraint(constraintPoly, "sum_{i=0}^{programLength} (selector(i) * (R'_j - R_j_OR_ALUres))", Opcode::NONE); + //} + + } + + void TraceConsistency::timeStampWitness(){ + const Algebra::FElem generator = Algebra::FElem(getGF2E_X()); + Algebra::Variable timeStamp0 = followingTraceVariables_.first_.timeStamp_; + Algebra::Variable timeStamp1 = followingTraceVariables_.second_.timeStamp_; + pb_->val(timeStamp1) = pb_->val(timeStamp0) * generator; + // Timestamp is reprented by g^i in order to know the degree of the element + // we keep a mapping between the FELem and i. Each time we increase the timestamp, + // we update the mapping in protoboard + unsigned int timeStampDegree = (pb_->val(timeStamp0) == Algebra::one()) ? 0 : pb_->getDegreeOfFElem(pb_->val(timeStamp0)); + pb_->addDegreeTranslation(pb_->val(timeStamp1), timeStampDegree + 1); + + } + + void TraceConsistency::pcWitness(unsigned int nextPC){ + Algebra::UnpackedWord pcUnpacked(followingTraceVariables_.second_.pc_); + const int pcLength = program_.pcLength(); + GADGETLIB_ASSERT((int)pcUnpacked.size() == pcLength, "TraceConsistency: PC Length should be at least log2Ciel(programLength - 1) + 1"); + for (int i = 0; i < pcLength; ++i){ + pb_->val(pcUnpacked[i]) = (nextPC & 1U) ? Algebra::one() : Algebra::zero(); + nextPC >>= 1; + } + } + + void TraceConsistency::registersWitness(unsigned int programLine){ + unsigned int numRegistersTrace1 = followingTraceVariables_.first_.registers_.size(); + unsigned int numRegistersTrace2 = followingTraceVariables_.second_.registers_.size(); + GADGETLIB_ASSERT(numRegistersTrace1 == numRegistersTrace2, "TraceConsistency: number of registers should be the same"); + Opcode opcode = program_.code()[programLine].opcode_; + unsigned int dest = program_.code()[programLine].destIdx_; + for (unsigned int i = 0; i < numRegistersTrace1; i++){ + Variable regiSecond = followingTraceVariables_.second_.registers_[i]; + Variable regiFirst = followingTraceVariables_.first_.registers_[i]; + if (dest != i){ + pb_->val(regiSecond) = pb_->val(regiFirst); + } + else{ + switch (opcode) + { + case Opcode::ADD: + case Opcode::SUB: + case Opcode::AND: + case Opcode::OR: + case Opcode::XOR: + case Opcode::SHL: + case Opcode::SHR: + case Opcode::MOV: + case Opcode::RESERVED_OPCODE_24: + pb_->val(regiSecond) = pb_->val(aluOutput_.result_); + break; + case Opcode::JMP: + case Opcode::CJMP: + case Opcode::CNJMP: + case Opcode::CMPE: + case Opcode::CMPA: + case Opcode::CMPAE: + case Opcode::CMPG: + case Opcode::STOREW: + case Opcode::ANSWER: + pb_->val(regiSecond) = pb_->val(regiFirst); + break; + case Opcode::LOADW: + pb_->val(regiSecond) = pb_->val(aluOutput_.value_); + break; + default: + GADGETLIB_FATAL("Trace Consistency: No Such opcode exists"); + break; + } + } + } + } + + GadgetPtr TraceConsistency::create(ProtoboardPtr pb, + const ALUOutput& aluOutput, + const FollowingTraceVariables& followingTraceVariables){ + GadgetPtr pGadget(new TraceConsistency(pb, aluOutput, followingTraceVariables)); + pGadget->init(); + return pGadget; + } + + + void TraceConsistency::setProgram(const TinyRAMProgram& program){ + program_ = program; + } + + void TraceConsistency::generateConstraints(){ + GADGETLIB_ASSERT(aluOutput_.flag_ == followingTraceVariables_.second_.flag_, "TraceConsistency: aluOutput.flag == followingTraceVariables_.second_.flag_"); + GADGETLIB_ASSERT(program_.size() > 0, "TraceConsistency: The program should be initialized"); + const Algebra::FElem generator = Algebra::FElem(getGF2E_X()); + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + timeStampConsistency(); + pcConsistency(); + registerConsistency(); + + } + + + void TraceConsistency::generateWitness(unsigned int programLine){ + GADGETLIB_ASSERT(program_.size() > 0, "TraceConsistency: The program should be initialized"); + GADGETLIB_ASSERT(programLine < program_.size(), "TraceCosistency: programLine should be less than the program Length "); + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + const Algebra::FElem generator = Algebra::FElem(getGF2E_X()); + timeStampWitness(); + Opcode opcode = program_.code()[programLine].opcode_; + if (opcode == Opcode::JMP || opcode == Opcode::CJMP || opcode == Opcode::CNJMP){ + unsigned int nextLine; + if (program_.code()[programLine].arg2isImmediate_){ + nextLine = program_.code()[programLine].arg2IdxOrImmediate_; + } + else{ + unsigned int regIndex = program_.code()[programLine].arg2IdxOrImmediate_; + nextLine = mapFieldElementToInteger(0, program_.pcLength(), pb_->val(followingTraceVariables_.first_.registers_[regIndex])); + } + + // Calculate PC + if (opcode == Opcode::JMP){ + pcWitness(nextLine); + } + else if (opcode == Opcode::CJMP){ + if (pb_->val(followingTraceVariables_.first_.flag_) == Algebra::one()){ + pcWitness(nextLine); + } + else{ + pcWitness(programLine + 1); + } + } + else{ + if (pb_->val(followingTraceVariables_.first_.flag_) == Algebra::zero()){ + pcWitness(nextLine); + } + else{ pcWitness(programLine + 1); } + } + } + else if (opcode == Opcode::ANSWER){ + pcWitness(programLine); + } + else{ + pcWitness(programLine + 1); + } + registersWitness(programLine); + + } + + diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/traceConsistency.hpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/traceConsistency.hpp new file mode 100644 index 0000000..d9e5de4 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/traceConsistency.hpp @@ -0,0 +1,43 @@ +#ifndef _COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_TRACECONSISTENCY_HPP_ +#define _COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_TRACECONSISTENCY_HPP_ + +#include +#include +#include "generalPurpose.hpp" +#include "TinyRAM/TinyRAMInstance.hpp" +using gadgetlib::Gadget; +using gadgetlib::ProtoboardPtr; + + + +class TraceConsistency : public Gadget{ +private: + ALUOutput aluOutput_; + FollowingTraceVariables followingTraceVariables_; + TinyRAMProgram program_; + + TraceConsistency(ProtoboardPtr pb, + const ALUOutput& aluOutput, + const FollowingTraceVariables& followingTraceVariables); + virtual void init(){}; + + + void timeStampConsistency(); + void pcConsistency(); + void registerConsistency(); + void timeStampWitness(); + void pcWitness(unsigned int nextPC); + void registersWitness(unsigned int programLine); + +public: + static GadgetPtr create(ProtoboardPtr pb, + const ALUOutput& aluOutput, + const FollowingTraceVariables& followingTraceVariables); + void setProgram(const TinyRAMProgram& program); + void generateConstraints(); + void generateWitness(unsigned int programLine); + +}; + + +#endif //_COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_TRACECONSISTENCY_HPP_ diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/transitionFunction.cpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/transitionFunction.cpp new file mode 100644 index 0000000..c5769df --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/transitionFunction.cpp @@ -0,0 +1,98 @@ +#include +#include "transitionFunction.hpp" +#include "ALU.hpp" +#include "ALUInputConsistency.hpp" +#include "traceConsistency.hpp" + + + +TransitionFunction::TransitionFunction(ProtoboardPtr pb, + const FollowingTraceVariables& followingTraceVariable, + const MemoryFollowingTraceVariables& memoryFollowingTraceVariables, + const TinyRAMProgram& program) : + Gadget(pb), + program_(program), + followingTraceVariable_(followingTraceVariable), + memoryFollowingTraceVariables_(memoryFollowingTraceVariables), + inputTraceLine_(followingTraceVariable.first_), + outputTraceLine_(followingTraceVariable.second_), + aluInput(followingTraceVariable.first_.flag_, followingTraceVariable.first_.pc_), + aluOutput(followingTraceVariable.second_.flag_, memoryFollowingTraceVariables_.first_.isMemOp_,memoryFollowingTraceVariables_.first_.address_, + memoryFollowingTraceVariables_.first_.isLoad_, memoryFollowingTraceVariables_.first_.value_){} + +GadgetPtr TransitionFunction::create(ProtoboardPtr pb, + const FollowingTraceVariables& followingTraceVariable, + const MemoryFollowingTraceVariables& memoryFollowingTraceVariables, + const TinyRAMProgram& program){ + GadgetPtr pGadget(new TransitionFunction(pb, followingTraceVariable, memoryFollowingTraceVariables,program)); + pGadget->init(); + return pGadget; +} + +void TransitionFunction::init(){ + aluInputConsistnecy_g_ = ALUInputConsistency::create(pb_, inputTraceLine_, aluInput); + alu_g_ = ALU_Gadget::create(pb_, aluInput, aluOutput); + traceConsistency_g_ = TraceConsistency::create(pb_, aluOutput, followingTraceVariable_); + (::std::dynamic_pointer_cast(aluInputConsistnecy_g_))->setProgram(program_); + (::std::dynamic_pointer_cast(alu_g_))->setProgram(program_); + (::std::dynamic_pointer_cast(traceConsistency_g_))->setProgram(program_); +} + +void TransitionFunction::generateConstraints(){ + aluInputConsistnecy_g_->generateConstraints(); + alu_g_->generateConstraints(); + traceConsistency_g_->generateConstraints(); +}; + +int TransitionFunction::calcPC(){ + const FElem g = Algebra::xFE();// Algebra::FElem(getGF2E_X()); + FElem x_i = Algebra::one(); + FElem result = Algebra::zero(); + for (unsigned int i = 0; i < followingTraceVariable_.first_.pc_.size(); i++){ + result += pb_->val(followingTraceVariable_.first_.pc_[i]) * x_i; + x_i *= g; + } + return mapFieldElementToInteger(0, followingTraceVariable_.first_.pc_.size(), result); + +} + +void TransitionFunction::generateWitness(unsigned int i){ + int codeLineNumber = calcPC(); + GADGETLIB_ASSERT(codeLineNumber < (long)program_.size(), "TransitionFunction: The code line number should be less than program.size()"); + (::std::dynamic_pointer_cast(aluInputConsistnecy_g_))->generateWitness(codeLineNumber); + (::std::dynamic_pointer_cast(alu_g_))->generateWitness(codeLineNumber); + (::std::dynamic_pointer_cast(traceConsistency_g_))->generateWitness(codeLineNumber); + + + // Update Memory Info + ::std::shared_ptr params = std::dynamic_pointer_cast(pb_->params()); + MemoryInfo memInfo; + memInfo.updateSerialNumber(i); + Opcode opcode = program_.code()[codeLineNumber].opcode_; + if (Opcode::STOREB == opcode || Opcode::STOREW == opcode || + Opcode::LOADB == opcode || Opcode::LOADW == opcode) { + // isMemOp = true + memInfo.updateIsMemOp(true); + // Update timeStamp; + FElem timestamp = pb_->val(followingTraceVariable_.first_.timeStamp_); + int degree = pb_->getDegreeOfFElem(timestamp); + memInfo.updateTimestamp(timestamp, degree); + // Update Address + FElem address; + if (program_.code()[codeLineNumber].arg2isImmediate_){ + address = mapIntegerToFieldElement(0, params->registerLength(), program_.code()[codeLineNumber].arg2IdxOrImmediate_); + } + else{ // Not immediate - The address is what is written in arg2 + int regNum = program_.code()[codeLineNumber].arg2IdxOrImmediate_; + address = pb_->val(followingTraceVariable_.first_.registers_[regNum]); + } + memInfo.updateAddress(address); + // Update value; + FElem value = pb_->loadValue(address); + // Update Is Load Opcode + bool isLoad = (Opcode::LOADB == opcode || Opcode::LOADW == opcode) ? true : false; + memInfo.updateIsLoadOp(isLoad); + } + pb_->addMemoryInfo(memInfo); + +}; diff --git a/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/transitionFunction.hpp b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/transitionFunction.hpp new file mode 100644 index 0000000..e0b9514 --- /dev/null +++ b/tinyram/stark-tinyram/src/TinyRAMtoBair/RamToContraintSystem/transitionFunction.hpp @@ -0,0 +1,50 @@ +#ifndef _COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_TRANSITIONFUNCTION_HPP_ +#define _COMMON_CONSTRAINTSLIB2_RAMTOCOSTRAINTSYSTEM_TRANSITIONFUNCTION_HPP_ + +#include +#include +#include "generalPurpose.hpp" +#include "TinyRAM/TinyRAMInstance.hpp" +using gadgetlib::Gadget; +using gadgetlib::ProtoboardPtr; + + + +class TransitionFunction : public Gadget{ +private: + TransitionFunction(ProtoboardPtr pb, + const FollowingTraceVariables& followingTraceVariable, + const MemoryFollowingTraceVariables& memoryFollowingTraceVariables, + const TinyRAMProgram& program); + + TinyRAMProgram program_; + FollowingTraceVariables followingTraceVariable_; + MemoryFollowingTraceVariables memoryFollowingTraceVariables_; + TraceVariables inputTraceLine_; + TraceVariables outputTraceLine_; + virtual void init(); + + // Inner Variables + ALUInput aluInput; + ALUOutput aluOutput; + GadgetPtr alu_g_; + GadgetPtr aluInputConsistnecy_g_; + GadgetPtr traceConsistency_g_; + + int calcPC(); + +public: + static GadgetPtr create(ProtoboardPtr pb, + const FollowingTraceVariables& followingTraceVariable, + const MemoryFollowingTraceVariables& memoryFollowingTraceVariables, + const TinyRAMProgram& program); + void generateConstraints(); + void generateWitness(unsigned int i); + +}; + + + + + +#endif diff --git a/tinyram/stark-tinyram/src/main.cpp b/tinyram/stark-tinyram/src/main.cpp new file mode 100644 index 0000000..e5d94d8 --- /dev/null +++ b/tinyram/stark-tinyram/src/main.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "TinyRAMtoBair/ConstraintSystemToBair/cs2Bair.hpp" +#include "TinyRAMtoBair/RamToContraintSystem/ALU.hpp" + +using std::cout; +using std::endl; +using std::string; +using std::stoul; +using std::shared_ptr; +using std::vector; +using std::move; + +void printHelp(const string exeName){ + cout<<"Usage:"<"< "<"< archParams_(make_shared(prog.archParams().numRegisters, trRegisterLen, trOpcodeLen, 16, 1)); + + gadgetlib::ProtoboardPtr pb_instance = Protoboard::create(archParams_); + cs2Bair cs2bair_instance(pb_instance, prog, int(gadgetlib::POW2(t) - 1), false); + + unique_ptr cs2bairConstraints_(new cs2BairConstraints(cs2bair_instance)); + unique_ptr cs2bairMemoryCS_(new cs2BairMemoryCS(cs2bair_instance)); + size_t numVars = (cs2bairConstraints_->numVars() / 2); + return libstark::BairInstance( + numVars, + t, + move(cs2bairConstraints_), + move(cs2bairMemoryCS_), + cs2bair_instance.getBoundaryConstraints(), + vector(numVars,Algebra::zero())); +} + +libstark::BairWitness constructWitness(const TinyRAMProgram& prog, const unsigned int t){ + resetALU_GadgetGlobalState(); + shared_ptr archParams_(make_shared(prog.archParams().numRegisters, trRegisterLen, trOpcodeLen, 16, 1)); + + gadgetlib::ProtoboardPtr pb_witness = Protoboard::create(archParams_); + cs2Bair cs2bair_witness(pb_witness, prog, int(gadgetlib::POW2(t) - 1), true); + unique_ptr cs2bairColoring_(new cs2BairColoring(cs2bair_witness)); + unique_ptr cs2bairMemory_(new cs2BairMemory(cs2bair_witness)); + + return libstark::BairWitness(move(cs2bairColoring_), move(cs2bairMemory_)); +} + +void execute(const string assemblyFile, const unsigned int t){ + initTinyRAMParamsFromEnvVariables(); + TinyRAMProgram program(assemblyFile, REGISTERS_NUMBER, trRegisterLen); + program.addInstructionsFromFile(assemblyFile); + + const auto bairInstance = constructInstance(program,t); + const auto bairWitness = constructWitness(program,t); + libstark::Protocols::executeProtocol(bairInstance,bairWitness,false,false,true); +} + +int main(int argc, char *argv[]) { + if(argc != 3){ + printHelp(argv[0]); + return 0; + } + + string assemblyFile(argv[1]); + unsigned int executionLenLog(stoul(argv[2])); + execute(assemblyFile,executionLenLog); + + return 0; +}