Blame qrng/niederreiter-2.c

Packit 67cb25
/* Author: G. Jungman
Packit 67cb25
 */
Packit 67cb25
/* Implement Niederreiter base 2 generator.
Packit 67cb25
 * See:
Packit 67cb25
 *   Bratley, Fox, Niederreiter, ACM Trans. Model. Comp. Sim. 2, 195 (1992)
Packit 67cb25
 */
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <gsl/gsl_qrng.h>
Packit 67cb25
Packit 67cb25
Packit 67cb25
#define NIED2_CHARACTERISTIC 2
Packit 67cb25
#define NIED2_BASE 2
Packit 67cb25
Packit 67cb25
#define NIED2_MAX_DIMENSION 12
Packit 67cb25
#define NIED2_MAX_PRIM_DEGREE 5
Packit 67cb25
#define NIED2_MAX_DEGREE 50
Packit 67cb25
Packit 67cb25
#define NIED2_BIT_COUNT 30
Packit 67cb25
#define NIED2_NBITS (NIED2_BIT_COUNT+1)
Packit 67cb25
Packit 67cb25
#define MAXV (NIED2_NBITS + NIED2_MAX_DEGREE)
Packit 67cb25
Packit 67cb25
/* Z_2 field operations */
Packit 67cb25
#define NIED2_ADD(x,y) (((x)+(y))%2)
Packit 67cb25
#define NIED2_MUL(x,y) (((x)*(y))%2)
Packit 67cb25
#define NIED2_SUB(x,y) NIED2_ADD((x),(y))
Packit 67cb25
Packit 67cb25
Packit 67cb25
static size_t nied2_state_size(unsigned int dimension);
Packit 67cb25
static int nied2_init(void * state, unsigned int dimension);
Packit 67cb25
static int nied2_get(void * state, unsigned int dimension, double * v);
Packit 67cb25
Packit 67cb25
Packit 67cb25
static const gsl_qrng_type nied2_type = 
Packit 67cb25
{
Packit 67cb25
  "niederreiter-base-2",
Packit 67cb25
  NIED2_MAX_DIMENSION,
Packit 67cb25
  nied2_state_size,
Packit 67cb25
  nied2_init,
Packit 67cb25
  nied2_get
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
const gsl_qrng_type * gsl_qrng_niederreiter_2 = &nied2_type;
Packit 67cb25
Packit 67cb25
/* primitive polynomials in binary encoding */
Packit 67cb25
static const int primitive_poly[NIED2_MAX_DIMENSION+1][NIED2_MAX_PRIM_DEGREE+1] =
Packit 67cb25
{
Packit 67cb25
  { 1, 0, 0, 0, 0, 0 },  /*  1               */
Packit 67cb25
  { 0, 1, 0, 0, 0, 0 },  /*  x               */
Packit 67cb25
  { 1, 1, 0, 0, 0, 0 },  /*  1 + x           */
Packit 67cb25
  { 1, 1, 1, 0, 0, 0 },  /*  1 + x + x^2     */
Packit 67cb25
  { 1, 1, 0, 1, 0, 0 },  /*  1 + x + x^3     */
Packit 67cb25
  { 1, 0, 1, 1, 0, 0 },  /*  1 + x^2 + x^3   */
Packit 67cb25
  { 1, 1, 0, 0, 1, 0 },  /*  1 + x + x^4     */
Packit 67cb25
  { 1, 0, 0, 1, 1, 0 },  /*  1 + x^3 + x^4   */
Packit 67cb25
  { 1, 1, 1, 1, 1, 0 },  /*  1 + x + x^2 + x^3 + x^4   */
Packit 67cb25
  { 1, 0, 1, 0, 0, 1 },  /*  1 + x^2 + x^5             */
Packit 67cb25
  { 1, 0, 0, 1, 0, 1 },  /*  1 + x^3 + x^5             */
Packit 67cb25
  { 1, 1, 1, 1, 0, 1 },  /*  1 + x + x^2 + x^3 + x^5   */
Packit 67cb25
  { 1, 1, 1, 0, 1, 1 }   /*  1 + x + x^2 + x^4 + x^5   */
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
/* degrees of primitive polynomials */
Packit 67cb25
static const int poly_degree[NIED2_MAX_DIMENSION+1] =
Packit 67cb25
{
Packit 67cb25
  0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 5, 5
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
Packit 67cb25
typedef struct
Packit 67cb25
{
Packit 67cb25
  unsigned int sequence_count;
Packit 67cb25
  int cj[NIED2_NBITS][NIED2_MAX_DIMENSION];
Packit 67cb25
  int nextq[NIED2_MAX_DIMENSION];
Packit 67cb25
} nied2_state_t;
Packit 67cb25
Packit 67cb25
Packit 67cb25
static size_t nied2_state_size(unsigned int dimension)
Packit 67cb25
{
Packit 67cb25
  return sizeof(nied2_state_t);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
Packit 67cb25
/* Multiply polynomials over Z_2.
Packit 67cb25
 * Notice use of a temporary vector,
Packit 67cb25
 * side-stepping aliasing issues when
Packit 67cb25
 * one of inputs is the same as the output
Packit 67cb25
 * [especially important in the original fortran version, I guess].
Packit 67cb25
 */
Packit 67cb25
static void poly_multiply(
Packit 67cb25
  const int pa[], int pa_degree,
Packit 67cb25
  const int pb[], int pb_degree,
Packit 67cb25
  int pc[], int  * pc_degree
Packit 67cb25
  )
Packit 67cb25
{
Packit 67cb25
  int j, k;
Packit 67cb25
  int pt[NIED2_MAX_DEGREE+1];
Packit 67cb25
  int pt_degree = pa_degree + pb_degree;
Packit 67cb25
Packit 67cb25
  for(k=0; k<=pt_degree; k++) {
Packit 67cb25
    int term = 0;
Packit 67cb25
    for(j=0; j<=k; j++) {
Packit 67cb25
      const int conv_term = NIED2_MUL(pa[k-j], pb[j]);
Packit 67cb25
      term = NIED2_ADD(term, conv_term);
Packit 67cb25
    }
Packit 67cb25
    pt[k] = term;
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  for(k=0; k<=pt_degree; k++) {
Packit 67cb25
    pc[k] = pt[k];
Packit 67cb25
  }
Packit 67cb25
  for(k=pt_degree+1; k<=NIED2_MAX_DEGREE; k++) {
Packit 67cb25
    pc[k] = 0;
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  *pc_degree = pt_degree;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
Packit 67cb25
/* Calculate the values of the constants V(J,R) as
Packit 67cb25
 * described in BFN section 3.3.
Packit 67cb25
 *
Packit 67cb25
 *   px = appropriate irreducible polynomial for current dimension
Packit 67cb25
 *   pb = polynomial defined in section 2.3 of BFN.
Packit 67cb25
 * pb is modified
Packit 67cb25
 */
Packit 67cb25
static void calculate_v(
Packit 67cb25
  const int px[], int px_degree,
Packit 67cb25
  int pb[], int * pb_degree,
Packit 67cb25
  int v[], int maxv
Packit 67cb25
  )
Packit 67cb25
{
Packit 67cb25
  const int nonzero_element = 1;    /* nonzero element of Z_2  */
Packit 67cb25
  const int arbitrary_element = 1;  /* arbitray element of Z_2 */
Packit 67cb25
Packit 67cb25
  /* The polynomial ph is px**(J-1), which is the value of B on arrival.
Packit 67cb25
   * In section 3.3, the values of Hi are defined with a minus sign:
Packit 67cb25
   * don't forget this if you use them later !
Packit 67cb25
   */
Packit 67cb25
  int ph[NIED2_MAX_DEGREE+1];
Packit 67cb25
  /* int ph_degree = *pb_degree; */
Packit 67cb25
  int bigm = *pb_degree;      /* m from section 3.3 */
Packit 67cb25
  int m;                      /* m from section 2.3 */
Packit 67cb25
  int r, k, kj;
Packit 67cb25
Packit 67cb25
  for(k=0; k<=NIED2_MAX_DEGREE; k++) {
Packit 67cb25
    ph[k] = pb[k];
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  /* Now multiply B by PX so B becomes PX**J.
Packit 67cb25
   * In section 2.3, the values of Bi are defined with a minus sign :
Packit 67cb25
   * don't forget this if you use them later !
Packit 67cb25
   */
Packit 67cb25
   poly_multiply(px, px_degree, pb, *pb_degree, pb, pb_degree);
Packit 67cb25
   m = *pb_degree;
Packit 67cb25
Packit 67cb25
  /* Now choose a value of Kj as defined in section 3.3.
Packit 67cb25
   * We must have 0 <= Kj < E*J = M.
Packit 67cb25
   * The limit condition on Kj does not seem very relevant
Packit 67cb25
   * in this program.
Packit 67cb25
   */
Packit 67cb25
  /* Quoting from BFN: "Our program currently sets each K_q
Packit 67cb25
   * equal to eq. This has the effect of setting all unrestricted
Packit 67cb25
   * values of v to 1."
Packit 67cb25
   * Actually, it sets them to the arbitrary chosen value.
Packit 67cb25
   * Whatever.
Packit 67cb25
   */
Packit 67cb25
  kj = bigm;
Packit 67cb25
Packit 67cb25
  /* Now choose values of V in accordance with
Packit 67cb25
   * the conditions in section 3.3.
Packit 67cb25
   */
Packit 67cb25
  for(r=0; r
Packit 67cb25
    v[r] = 0;
Packit 67cb25
  }
Packit 67cb25
  v[kj] = 1;
Packit 67cb25
Packit 67cb25
Packit 67cb25
  if(kj >= bigm) {
Packit 67cb25
    for(r=kj+1; r
Packit 67cb25
      v[r] = arbitrary_element;
Packit 67cb25
    }
Packit 67cb25
  }
Packit 67cb25
  else {
Packit 67cb25
    /* This block is never reached. */
Packit 67cb25
Packit 67cb25
    int term = NIED2_SUB(0, ph[kj]);
Packit 67cb25
Packit 67cb25
    for(r=kj+1; r
Packit 67cb25
      v[r] = arbitrary_element;
Packit 67cb25
Packit 67cb25
      /* Check the condition of section 3.3,
Packit 67cb25
       * remembering that the H's have the opposite sign.  [????????]
Packit 67cb25
       */
Packit 67cb25
      term = NIED2_SUB(term, NIED2_MUL(ph[r], v[r]));
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
    /* Now v[bigm] != term. */
Packit 67cb25
    v[bigm] = NIED2_ADD(nonzero_element, term);
Packit 67cb25
Packit 67cb25
    for(r=bigm+1; r
Packit 67cb25
      v[r] = arbitrary_element;
Packit 67cb25
    }
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  /* Calculate the remaining V's using the recursion of section 2.3,
Packit 67cb25
   * remembering that the B's have the opposite sign.
Packit 67cb25
   */
Packit 67cb25
  for(r=0; r<=maxv-m; r++) {
Packit 67cb25
    int term = 0;
Packit 67cb25
    for(k=0; k
Packit 67cb25
      term = NIED2_SUB(term, NIED2_MUL(pb[k], v[r+k]));
Packit 67cb25
    }
Packit 67cb25
    v[r+m] = term;
Packit 67cb25
  }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
Packit 67cb25
static void calculate_cj(nied2_state_t * ns, unsigned int dimension)
Packit 67cb25
{
Packit 67cb25
  int ci[NIED2_NBITS][NIED2_NBITS];
Packit 67cb25
  int v[MAXV+1];
Packit 67cb25
  int r;
Packit 67cb25
  unsigned int i_dim;
Packit 67cb25
Packit 67cb25
  for(i_dim=0; i_dim
Packit 67cb25
Packit 67cb25
    const int poly_index = i_dim + 1;
Packit 67cb25
    int j, k;
Packit 67cb25
Packit 67cb25
    /* Niederreiter (page 56, after equation (7), defines two
Packit 67cb25
     * variables Q and U.  We do not need Q explicitly, but we
Packit 67cb25
     * do need U.
Packit 67cb25
     */
Packit 67cb25
    int u = 0;
Packit 67cb25
Packit 67cb25
    /* For each dimension, we need to calculate powers of an
Packit 67cb25
     * appropriate irreducible polynomial, see Niederreiter
Packit 67cb25
     * page 65, just below equation (19).
Packit 67cb25
     * Copy the appropriate irreducible polynomial into PX,
Packit 67cb25
     * and its degree into E.  Set polynomial B = PX ** 0 = 1.
Packit 67cb25
     * M is the degree of B.  Subsequently B will hold higher
Packit 67cb25
     * powers of PX.
Packit 67cb25
     */
Packit 67cb25
    int pb[NIED2_MAX_DEGREE+1];
Packit 67cb25
    int px[NIED2_MAX_DEGREE+1];
Packit 67cb25
    int px_degree = poly_degree[poly_index];
Packit 67cb25
    int pb_degree = 0;
Packit 67cb25
Packit 67cb25
    for(k=0; k<=px_degree; k++) {
Packit 67cb25
      px[k] = primitive_poly[poly_index][k];
Packit 67cb25
      pb[k] = 0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
    for (;k
Packit 67cb25
      px[k] = 0;
Packit 67cb25
      pb[k] = 0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
    pb[0] = 1;
Packit 67cb25
Packit 67cb25
    for(j=0; j
Packit 67cb25
Packit 67cb25
      /* If U = 0, we need to set B to the next power of PX
Packit 67cb25
       * and recalculate V.
Packit 67cb25
       */
Packit 67cb25
      if(u == 0) calculate_v(px, px_degree, pb, &pb_degree, v, MAXV);
Packit 67cb25
Packit 67cb25
      /* Now C is obtained from V.  Niederreiter
Packit 67cb25
       * obtains A from V (page 65, near the bottom), and then gets
Packit 67cb25
       * C from A (page 56, equation (7)).  However this can be done
Packit 67cb25
       * in one step.  Here CI(J,R) corresponds to
Packit 67cb25
       * Niederreiter's C(I,J,R).
Packit 67cb25
       */
Packit 67cb25
      for(r=0; r
Packit 67cb25
        ci[r][j] = v[r+u];
Packit 67cb25
      }
Packit 67cb25
Packit 67cb25
      /* Advance Niederreiter's state variables. */
Packit 67cb25
      ++u;
Packit 67cb25
      if(u == px_degree) u = 0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
    /* The array CI now holds the values of C(I,J,R) for this value
Packit 67cb25
     * of I.  We pack them into array CJ so that CJ(I,R) holds all
Packit 67cb25
     * the values of C(I,J,R) for J from 1 to NBITS.
Packit 67cb25
     */
Packit 67cb25
    for(r=0; r
Packit 67cb25
      int term = 0;
Packit 67cb25
      for(j=0; j
Packit 67cb25
        term = 2*term + ci[r][j];
Packit 67cb25
      }
Packit 67cb25
      ns->cj[r][i_dim] = term;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
Packit 67cb25
static int nied2_init(void * state, unsigned int dimension)
Packit 67cb25
{
Packit 67cb25
  nied2_state_t * n_state = (nied2_state_t *) state;
Packit 67cb25
  unsigned int i_dim;
Packit 67cb25
Packit 67cb25
  if(dimension < 1 || dimension > NIED2_MAX_DIMENSION) return GSL_EINVAL;
Packit 67cb25
Packit 67cb25
  calculate_cj(n_state, dimension);
Packit 67cb25
Packit 67cb25
  for(i_dim=0; i_dim<dimension; i_dim++) n_state->nextq[i_dim] = 0;
Packit 67cb25
  n_state->sequence_count = 0;
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
Packit 67cb25
static int nied2_get(void * state, unsigned int dimension, double * v)
Packit 67cb25
{
Packit 67cb25
  static const double recip = 1.0/(double)(1U << NIED2_NBITS); /* 2^(-nbits) */
Packit 67cb25
  nied2_state_t * n_state = (nied2_state_t *) state;
Packit 67cb25
  int r;
Packit 67cb25
  int c;
Packit 67cb25
  unsigned int i_dim;
Packit 67cb25
Packit 67cb25
  /* Load the result from the saved state. */
Packit 67cb25
  for(i_dim=0; i_dim
Packit 67cb25
    v[i_dim] = n_state->nextq[i_dim] * recip;
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  /* Find the position of the least-significant zero in sequence_count.
Packit 67cb25
   * This is the bit that changes in the Gray-code representation as
Packit 67cb25
   * the count is advanced.
Packit 67cb25
   */
Packit 67cb25
  r = 0;
Packit 67cb25
  c = n_state->sequence_count;
Packit 67cb25
  while(1) {
Packit 67cb25
    if((c % 2) == 1) {
Packit 67cb25
      ++r;
Packit 67cb25
      c /= 2;
Packit 67cb25
    }
Packit 67cb25
    else break;
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  if(r >= NIED2_NBITS) return GSL_EFAILED; /* FIXME: better error code here */
Packit 67cb25
Packit 67cb25
  /* Calculate the next state. */
Packit 67cb25
  for(i_dim=0; i_dim
Packit 67cb25
    n_state->nextq[i_dim] ^= n_state->cj[r][i_dim];
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  n_state->sequence_count++;
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}