int test_defaults_dp() { dgs_disc_gauss_dp_t *self; self = dgs_disc_gauss_dp_init(3.0, 0, 6, DGS_DISC_GAUSS_DEFAULT); if (self->algorithm != DGS_DISC_GAUSS_UNIFORM_TABLE) dgs_die("automatic choice of uniform table algorithm failed (%d)", self->algorithm); dgs_disc_gauss_dp_clear(self); self = dgs_disc_gauss_dp_init(3.0, 0, 1<<10, DGS_DISC_GAUSS_DEFAULT); if (self->algorithm != DGS_DISC_GAUSS_UNIFORM_TABLE) dgs_die("automatic choice of uniform table algorithm failed (%d)", self->algorithm); dgs_disc_gauss_dp_clear(self); self = dgs_disc_gauss_dp_init(3.0, 0, 1<<14, DGS_DISC_GAUSS_DEFAULT); if (self->algorithm != DGS_DISC_GAUSS_UNIFORM_LOGTABLE) dgs_die("automatic choice of uniform table algorithm failed (%d)", self->algorithm); dgs_disc_gauss_dp_clear(self); double sigma2 = sqrt(1.0/(2*log(2.0))); self = dgs_disc_gauss_dp_init(1024*sigma2, 0, 6, DGS_DISC_GAUSS_DEFAULT); if (self->algorithm != DGS_DISC_GAUSS_SIGMA2_LOGTABLE) dgs_die("automatic choice of uniform table algorithm failed (%d)", self->algorithm); dgs_disc_gauss_dp_clear(self); printf("passed\n"); return 0; }
int test_uniform_boundaries_dp(double sigma, double c, size_t tau, dgs_disc_gauss_alg_t algorithm) { dgs_disc_gauss_dp_t *self = dgs_disc_gauss_dp_init(sigma, c, tau, algorithm); printf("σ: %6.2f, c: %6.2f. τ: %2ld, precision: double, algorithm: %d\n", self->sigma, self->c, self->tau, self->algorithm); long lower_bound = ((long)self->c) - ceil(self->sigma * self->tau); long upper_bound = ((long)self->c) + ceil(self->sigma * self->tau); for(size_t i=0; i<NTRIALS; i++) { long r = self->call(self); if(__DGS_UNLIKELY(r < lower_bound)) dgs_die("r (%ld) < lower_bound (%ld)", r, lower_bound); else if(__DGS_UNLIKELY(r > upper_bound)) dgs_die("r (%ld) > upper_bound (%ld)", r, upper_bound); } return 0; }
int test_mean_dp(double sigma, double c, size_t tau, dgs_disc_gauss_alg_t algorithm) { printf("σ: %6.2f, c: %6.2f. τ: %2ld, precision: double, algorithm: %d\n",sigma, c, tau, algorithm); dgs_disc_gauss_dp_t *self = dgs_disc_gauss_dp_init(sigma, c, tau, algorithm); double mean = 0.0; for(size_t i=0; i<NTRIALS; i++) { long r = self->call(self); mean += r; } mean /=NTRIALS; if(fabs(mean - c) > TOLERANCE) dgs_die("expected mean %6.2f but got %6.2f",c, mean); return 0; }
int test_ratios_dp(double sigma, size_t tau, dgs_disc_gauss_alg_t algorithm) { printf("σ: %6.2f, c: 0.0. τ: %2ld, precision: double, algorithm: %d\n",sigma, tau, algorithm); dgs_disc_gauss_dp_t *self = dgs_disc_gauss_dp_init(sigma, 0, tau, algorithm); double ctr[2*BOUND+1]; for(size_t i=0; i<NTRIALS; i++) { long r = self->call(self); if (abs(r) <= BOUND) ctr[r+BOUND] += 1; } for(long i=-BOUND; i<=BOUND; i++) { double left = ctr[BOUND+1]/ctr[BOUND+i]; double right = RHO(0)/RHO(i); if (fabs(log(left/right)) >= 0.4) dgs_die("exp(-((-c)²)/(2σ²))/exp(-((%d-c)²)/(2σ²)) = %7.5f != %7.5f (%7.5f)", i, right, left, fabs(log(left/right))); } return 0; }
dgsl_rot_mp_t *dgsl_rot_mp_init(const long n, const fmpz_poly_t B, mpfr_t sigma, fmpq_poly_t c, const dgsl_alg_t algorithm, const oz_flag_t flags) { assert(mpfr_cmp_ui(sigma, 0) > 0); dgsl_rot_mp_t *self = (dgsl_rot_mp_t*)calloc(1, sizeof(dgsl_rot_mp_t)); if(!self) dgs_die("out of memory"); dgsl_alg_t alg = algorithm; self->n = n; self->prec = mpfr_get_prec(sigma); fmpz_poly_init(self->B); fmpz_poly_set(self->B, B); if(fmpz_poly_length(self->B) > n) dgs_die("polynomial is longer than length n"); else fmpz_poly_realloc(self->B, n); fmpz_poly_init(self->c_z); fmpq_poly_init(self->c); mpfr_init2(self->sigma, self->prec); mpfr_set(self->sigma, sigma, MPFR_RNDN); if (alg == DGSL_DETECT) { if (fmpz_poly_is_one(self->B) && (c && fmpq_poly_is_zero(c))) { alg = DGSL_IDENTITY; } else if (c && fmpq_poly_is_zero(c)) alg = DGSL_INLATTICE; else alg = DGSL_COSET; //TODO: we could test for lattice membership here } size_t tau = 3; if (2*ceil(sqrt(log2((double)n))) > tau) tau = 2*ceil(sqrt(log2((double)n))); switch(alg) { case DGSL_IDENTITY: { self->D = (dgs_disc_gauss_mp_t**)calloc(1, sizeof(dgs_disc_gauss_mp_t*)); mpfr_t c_; mpfr_init2(c_, self->prec); mpfr_set_d(c_, 0.0, MPFR_RNDN); self->D[0] = dgs_disc_gauss_mp_init(self->sigma, c_, tau, DGS_DISC_GAUSS_DEFAULT); self->call = dgsl_rot_mp_call_identity; mpfr_clear(c_); break; } case DGSL_GPV_INLATTICE: { self->D = (dgs_disc_gauss_mp_t**)calloc(n, sizeof(dgs_disc_gauss_mp_t*)); if (c && !fmpq_poly_is_zero(c)) { fmpq_t c_i; fmpq_init(c_i); for(int i=0; i<n; i++) { fmpq_poly_get_coeff_fmpq(c_i, c, i); fmpz_poly_set_coeff_fmpz(self->c_z, i, fmpq_numref(c_i)); } fmpq_clear(c_i); } mpfr_mat_t G; mpfr_mat_init(G, n, n, self->prec); mpfr_mat_set_fmpz_poly(G, B); mpfr_mat_gso(G, MPFR_RNDN); mpfr_t sigma_; mpfr_init2(sigma_, self->prec); mpfr_t norm; mpfr_init2(norm, self->prec); mpfr_t c_; mpfr_init2(c_, self->prec); mpfr_set_d(c_, 0.0, MPFR_RNDN); for(long i=0; i<n; i++) { _mpfr_vec_2norm(norm, G->rows[i], n, MPFR_RNDN); assert(mpfr_cmp_d(norm, 0.0) > 0); mpfr_div(sigma_, self->sigma, norm, MPFR_RNDN); assert(mpfr_cmp_d(sigma_, 0.0) > 0); self->D[i] = dgs_disc_gauss_mp_init(sigma_, c_, tau, DGS_DISC_GAUSS_DEFAULT); } mpfr_clear(sigma_); mpfr_clear(norm); mpfr_clear(c_); mpfr_mat_clear(G); self->call = dgsl_rot_mp_call_gpv_inlattice; break; } case DGSL_INLATTICE: { fmpq_poly_init(self->sigma_sqrt); long r= 2*ceil(sqrt(log(n))); fmpq_poly_t Bq; fmpq_poly_init(Bq); fmpq_poly_set_fmpz_poly(Bq, self->B); fmpq_poly_oz_invert_approx(self->B_inv, Bq, n, self->prec, flags); fmpq_poly_clear(Bq); _dgsl_rot_mp_sqrt_sigma_2(self->sigma_sqrt, self->B, sigma, r, n, self->prec, flags); mpfr_init2(self->r_f, self->prec); mpfr_set_ui(self->r_f, r, MPFR_RNDN); self->call = dgsl_rot_mp_call_inlattice; break; } case DGSL_COSET: dgs_die("not implemented"); default: dgs_die("not implemented"); } return self; }
dgsl_mp_t *dgsl_mp_init(const fmpz_mat_t B, mpfr_t sigma, mpfr_t *c, const dgsl_alg_t algorithm) { assert(mpfr_cmp_ui(sigma, 0) > 0); dgsl_mp_t *self = (dgsl_mp_t*)calloc(1, sizeof(dgsl_mp_t)); if(!self) dgs_die("out of memory"); dgsl_alg_t alg = algorithm; long m = fmpz_mat_nrows(B); long n = fmpz_mat_ncols(B); const mpfr_prec_t prec = mpfr_get_prec(sigma); fmpz_mat_init_set(self->B, B); self->c_z = _fmpz_vec_init(n); self->c = _mpfr_vec_init(n, prec); mpfr_init2(self->sigma, prec); mpfr_set(self->sigma, sigma, MPFR_RNDN); if (alg == DGSL_DETECT) { if (fmpz_mat_is_one(self->B)) { alg = DGSL_IDENTITY; } else if (_mpfr_vec_is_zero(c, n)) alg = DGSL_INLATTICE; else alg = DGSL_COSET; //TODO: we could test for lattice membership here } mpfr_t c_; mpfr_init2(c_, prec); size_t tau = 3; if (2*ceil(sqrt(log2((double)n))) > tau) tau = 2*ceil(sqrt(log2((double)n))); switch(alg) { case DGSL_IDENTITY: self->D = (dgs_disc_gauss_mp_t**)calloc(1, sizeof(dgs_disc_gauss_mp_t*)); mpfr_set_d(c_, 0.0, MPFR_RNDN); self->D[0] = dgs_disc_gauss_mp_init(self->sigma, c_, tau, DGS_DISC_GAUSS_DEFAULT); self->call = dgsl_mp_call_identity; break; case DGSL_INLATTICE: self->D = (dgs_disc_gauss_mp_t**)calloc(m, sizeof(dgs_disc_gauss_mp_t*)); if (c) _fmpz_vec_set_mpfr_vec(self->c_z, c, n); mpfr_mat_t G; mpfr_mat_init(G, m, n, prec); mpfr_mat_set_fmpz_mat(G, B); mpfr_mat_gso(G, MPFR_RNDN); mpfr_t sigma_; mpfr_init2(sigma_, prec); mpfr_t norm; mpfr_init2(norm, prec); mpfr_set_d(c_, 0.0, MPFR_RNDN); for(long i=0; i<m; i++) { _mpfr_vec_2norm(norm, G->rows[i], n, MPFR_RNDN); assert(mpfr_cmp_d(norm, 0.0) > 0); mpfr_div(sigma_, self->sigma, norm, MPFR_RNDN); assert(mpfr_cmp_d(sigma_, 0.0) > 0); self->D[i] = dgs_disc_gauss_mp_init(sigma_, c_, tau, DGS_DISC_GAUSS_DEFAULT); } mpfr_clear(sigma_); mpfr_clear(norm); mpfr_mat_clear(G); self->call = dgsl_mp_call_inlattice; break; case DGSL_COSET: mpfr_mat_init(self->G, m, n, prec); mpfr_mat_set_fmpz_mat(self->G, B); mpfr_mat_gso(self->G, MPFR_RNDN); self->call = dgsl_mp_call_coset; break; default: dgs_die("not implemented"); } mpfr_clear(c_); return self; }
dgs_disc_gauss_dp_t *dgs_disc_gauss_dp_init(double sigma, double c, size_t tau, dgs_disc_gauss_alg_t algorithm) { if (sigma <= 0.0) dgs_die("sigma must be > 0"); if (tau == 0) dgs_die("tau must be > 0"); size_t upper_bound; dgs_disc_gauss_dp_t *self = (dgs_disc_gauss_dp_t*)calloc(sizeof(dgs_disc_gauss_dp_t),1); if (!self) dgs_die("out of memory"); self->sigma = sigma; self->c = c; self->c_z = (long)c; self->c_r = self->c - ((double)self->c_z); self->tau = tau; switch(algorithm) { case DGS_DISC_GAUSS_UNIFORM_ONLINE: self->call = dgs_disc_gauss_dp_call_uniform_online; upper_bound = ceil(self->sigma*tau) + 1; self->upper_bound = upper_bound; self->upper_bound_minus_one = upper_bound - 1; self->two_upper_bound_minus_one = 2*upper_bound - 1; self->f = -1.0/(2.0*(self->sigma*self->sigma)); break; case DGS_DISC_GAUSS_UNIFORM_TABLE: self->call = dgs_disc_gauss_dp_call_uniform_table; upper_bound = ceil(self->sigma*tau) + 1; self->upper_bound = upper_bound; self->upper_bound_minus_one = upper_bound - 1; self->two_upper_bound_minus_one = 2*upper_bound - 1; self->B = dgs_bern_uniform_init(0); self->f = -1.0/(2.0*(sigma*sigma)); if(self->c_r == 0) { self->call = dgs_disc_gauss_dp_call_uniform_table; self->rho = (double*)malloc(sizeof(double)*self->upper_bound); if (!self->rho) { dgs_disc_gauss_dp_clear(self); dgs_die("out of memory"); } for(unsigned long x=0; x<self->upper_bound; x++) { self->rho[x] = exp( (((double)x) - self->c_r) * (((double)x) - self->c_r) * self->f); } self->rho[0]/= 2.0; } else { self->call = dgs_disc_gauss_dp_call_uniform_table_offset; self->rho = (double*)malloc(sizeof(double)*self->two_upper_bound_minus_one); if (!self->rho) { dgs_disc_gauss_dp_clear(self); dgs_die("out of memory"); } long absmax = self->upper_bound_minus_one; for(long x=-absmax; x<=absmax; x++) { self->rho[x+self->upper_bound_minus_one] = exp( (((double)x) - self->c_r) * (((double)x) - self->c_r) * self->f); } } break; case DGS_DISC_GAUSS_UNIFORM_LOGTABLE: self->call = dgs_disc_gauss_dp_call_uniform_logtable; if (fabs(self->c_r) > DGS_DISC_GAUSS_INTEGER_CUTOFF) { dgs_disc_gauss_dp_clear(self); dgs_die("algorithm DGS_DISC_GAUSS_UNIFORM_LOGTABLE requires c%1 == 0"); } upper_bound = ceil(self->sigma*tau) + 1; self->upper_bound = upper_bound; self->upper_bound_minus_one = upper_bound - 1; self->two_upper_bound_minus_one = 2*upper_bound - 1; _dgs_disc_gauss_dp_init_bexp(self, self->sigma, self->upper_bound); break; case DGS_DISC_GAUSS_SIGMA2_LOGTABLE: { self->call = dgs_disc_gauss_dp_call_sigma2_logtable; if (fabs(self->c_r) > DGS_DISC_GAUSS_INTEGER_CUTOFF) { dgs_disc_gauss_dp_clear(self); dgs_die("algorithm DGS_DISC_GAUSS_SIGMA2_LOGTABLE requires c%1 == 0"); } double sigma2 = sqrt(1.0/(2*log(2.0))); double k = sigma/sigma2; self->k = round(k); self->sigma = self->k * sigma2; upper_bound = ceil(self->sigma*tau) + 1; self->upper_bound = upper_bound; self->upper_bound_minus_one = upper_bound - 1; self->two_upper_bound_minus_one = 2*upper_bound - 1; _dgs_disc_gauss_dp_init_bexp(self, self->sigma, self->upper_bound); self->B = dgs_bern_uniform_init(0); self->D2 = dgs_disc_gauss_sigma2p_init(); break; } default: dgs_disc_gauss_dp_clear(self); dgs_die("unknown algorithm %d", algorithm); } return self; }