summaryrefslogtreecommitdiff
path: root/gsl-1.9/qrng/niederreiter-2.c
blob: 1a28009f1fd349ae1de15dc8b90d372ccd8c5be6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/* Author: G. Jungman
 */
/* Implement Niederreiter base 2 generator.
 * See:
 *   Bratley, Fox, Niederreiter, ACM Trans. Model. Comp. Sim. 2, 195 (1992)
 */
#include <config.h>
#include <gsl/gsl_qrng.h>


#define NIED2_CHARACTERISTIC 2
#define NIED2_BASE 2

#define NIED2_MAX_DIMENSION 12
#define NIED2_MAX_PRIM_DEGREE 5
#define NIED2_MAX_DEGREE 50

#define NIED2_BIT_COUNT 30
#define NIED2_NBITS (NIED2_BIT_COUNT+1)

#define MAXV (NIED2_NBITS + NIED2_MAX_DEGREE)

/* Z_2 field operations */
#define NIED2_ADD(x,y) (((x)+(y))%2)
#define NIED2_MUL(x,y) (((x)*(y))%2)
#define NIED2_SUB(x,y) NIED2_ADD((x),(y))


static size_t nied2_state_size(unsigned int dimension);
static int nied2_init(void * state, unsigned int dimension);
static int nied2_get(void * state, unsigned int dimension, double * v);


static const gsl_qrng_type nied2_type = 
{
  "niederreiter-base-2",
  NIED2_MAX_DIMENSION,
  nied2_state_size,
  nied2_init,
  nied2_get
};

const gsl_qrng_type * gsl_qrng_niederreiter_2 = &nied2_type;

/* primitive polynomials in binary encoding */
static const int primitive_poly[NIED2_MAX_DIMENSION+1][NIED2_MAX_PRIM_DEGREE+1] =
{
  { 1, 0, 0, 0, 0, 0 },  /*  1               */
  { 0, 1, 0, 0, 0, 0 },  /*  x               */
  { 1, 1, 0, 0, 0, 0 },  /*  1 + x           */
  { 1, 1, 1, 0, 0, 0 },  /*  1 + x + x^2     */
  { 1, 1, 0, 1, 0, 0 },  /*  1 + x + x^3     */
  { 1, 0, 1, 1, 0, 0 },  /*  1 + x^2 + x^3   */
  { 1, 1, 0, 0, 1, 0 },  /*  1 + x + x^4     */
  { 1, 0, 0, 1, 1, 0 },  /*  1 + x^3 + x^4   */
  { 1, 1, 1, 1, 1, 0 },  /*  1 + x + x^2 + x^3 + x^4   */
  { 1, 0, 1, 0, 0, 1 },  /*  1 + x^2 + x^5             */
  { 1, 0, 0, 1, 0, 1 },  /*  1 + x^3 + x^5             */
  { 1, 1, 1, 1, 0, 1 },  /*  1 + x + x^2 + x^3 + x^5   */
  { 1, 1, 1, 0, 1, 1 }   /*  1 + x + x^2 + x^4 + x^5   */
};

/* degrees of primitive polynomials */
static const int poly_degree[NIED2_MAX_DIMENSION+1] =
{
  0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 5, 5
};


typedef struct
{
  unsigned int sequence_count;
  int cj[NIED2_NBITS][NIED2_MAX_DIMENSION];
  int nextq[NIED2_MAX_DIMENSION];
} nied2_state_t;


static size_t nied2_state_size(unsigned int dimension)
{
  return sizeof(nied2_state_t);
}


/* Multiply polynomials over Z_2.
 * Notice use of a temporary vector,
 * side-stepping aliasing issues when
 * one of inputs is the same as the output
 * [especially important in the original fortran version, I guess].
 */
static void poly_multiply(
  const int pa[], int pa_degree,
  const int pb[], int pb_degree,
  int pc[], int  * pc_degree
  )
{
  int j, k;
  int pt[NIED2_MAX_DEGREE+1];
  int pt_degree = pa_degree + pb_degree;

  for(k=0; k<=pt_degree; k++) {
    int term = 0;
    for(j=0; j<=k; j++) {
      const int conv_term = NIED2_MUL(pa[k-j], pb[j]);
      term = NIED2_ADD(term, conv_term);
    }
    pt[k] = term;
  }

  for(k=0; k<=pt_degree; k++) {
    pc[k] = pt[k];
  }
  for(k=pt_degree+1; k<=NIED2_MAX_DEGREE; k++) {
    pc[k] = 0;
  }

  *pc_degree = pt_degree;
}


/* Calculate the values of the constants V(J,R) as
 * described in BFN section 3.3.
 *
 *   px = appropriate irreducible polynomial for current dimension
 *   pb = polynomial defined in section 2.3 of BFN.
 * pb is modified
 */
static void calculate_v(
  const int px[], int px_degree,
  int pb[], int * pb_degree,
  int v[], int maxv
  )
{
  const int nonzero_element = 1;    /* nonzero element of Z_2  */
  const int arbitrary_element = 1;  /* arbitray element of Z_2 */

  /* The polynomial ph is px**(J-1), which is the value of B on arrival.
   * In section 3.3, the values of Hi are defined with a minus sign:
   * don't forget this if you use them later !
   */
  int ph[NIED2_MAX_DEGREE+1];
  /* int ph_degree = *pb_degree; */
  int bigm = *pb_degree;      /* m from section 3.3 */
  int m;                      /* m from section 2.3 */
  int r, k, kj;

  for(k=0; k<=NIED2_MAX_DEGREE; k++) {
    ph[k] = pb[k];
  }

  /* Now multiply B by PX so B becomes PX**J.
   * In section 2.3, the values of Bi are defined with a minus sign :
   * don't forget this if you use them later !
   */
   poly_multiply(px, px_degree, pb, *pb_degree, pb, pb_degree);
   m = *pb_degree;

  /* Now choose a value of Kj as defined in section 3.3.
   * We must have 0 <= Kj < E*J = M.
   * The limit condition on Kj does not seem very relevant
   * in this program.
   */
  /* Quoting from BFN: "Our program currently sets each K_q
   * equal to eq. This has the effect of setting all unrestricted
   * values of v to 1."
   * Actually, it sets them to the arbitrary chosen value.
   * Whatever.
   */
  kj = bigm;

  /* Now choose values of V in accordance with
   * the conditions in section 3.3.
   */
  for(r=0; r<kj; r++) {
    v[r] = 0;
  }
  v[kj] = 1;


  if(kj >= bigm) {
    for(r=kj+1; r<m; r++) {
      v[r] = arbitrary_element;
    }
  }
  else {
    /* This block is never reached. */

    int term = NIED2_SUB(0, ph[kj]);

    for(r=kj+1; r<bigm; r++) {
      v[r] = arbitrary_element;

      /* Check the condition of section 3.3,
       * remembering that the H's have the opposite sign.  [????????]
       */
      term = NIED2_SUB(term, NIED2_MUL(ph[r], v[r]));
    }

    /* Now v[bigm] != term. */
    v[bigm] = NIED2_ADD(nonzero_element, term);

    for(r=bigm+1; r<m; r++) {
      v[r] = arbitrary_element;
    }
  }

  /* Calculate the remaining V's using the recursion of section 2.3,
   * remembering that the B's have the opposite sign.
   */
  for(r=0; r<=maxv-m; r++) {
    int term = 0;
    for(k=0; k<m; k++) {
      term = NIED2_SUB(term, NIED2_MUL(pb[k], v[r+k]));
    }
    v[r+m] = term;
  }
}


static void calculate_cj(nied2_state_t * ns, unsigned int dimension)
{
  int ci[NIED2_NBITS][NIED2_NBITS];
  int v[MAXV+1];
  int r;
  unsigned int i_dim;

  for(i_dim=0; i_dim<dimension; i_dim++) {

    const int poly_index = i_dim + 1;
    int j, k;

    /* Niederreiter (page 56, after equation (7), defines two
     * variables Q and U.  We do not need Q explicitly, but we
     * do need U.
     */
    int u = 0;

    /* For each dimension, we need to calculate powers of an
     * appropriate irreducible polynomial, see Niederreiter
     * page 65, just below equation (19).
     * Copy the appropriate irreducible polynomial into PX,
     * and its degree into E.  Set polynomial B = PX ** 0 = 1.
     * M is the degree of B.  Subsequently B will hold higher
     * powers of PX.
     */
    int pb[NIED2_MAX_DEGREE+1];
    int px[NIED2_MAX_DEGREE+1];
    int px_degree = poly_degree[poly_index];
    int pb_degree = 0;

    for(k=0; k<=px_degree; k++) {
      px[k] = primitive_poly[poly_index][k];
      pb[k] = 0;
    }

    for (;k<NIED2_MAX_DEGREE+1;k++) {
      px[k] = 0;
      pb[k] = 0;
    }

    pb[0] = 1;

    for(j=0; j<NIED2_NBITS; j++) {

      /* If U = 0, we need to set B to the next power of PX
       * and recalculate V.
       */
      if(u == 0) calculate_v(px, px_degree, pb, &pb_degree, v, MAXV);

      /* Now C is obtained from V.  Niederreiter
       * obtains A from V (page 65, near the bottom), and then gets
       * C from A (page 56, equation (7)).  However this can be done
       * in one step.  Here CI(J,R) corresponds to
       * Niederreiter's C(I,J,R).
       */
      for(r=0; r<NIED2_NBITS; r++) {
        ci[r][j] = v[r+u];
      }

      /* Advance Niederreiter's state variables. */
      ++u;
      if(u == px_degree) u = 0;
    }

    /* The array CI now holds the values of C(I,J,R) for this value
     * of I.  We pack them into array CJ so that CJ(I,R) holds all
     * the values of C(I,J,R) for J from 1 to NBITS.
     */
    for(r=0; r<NIED2_NBITS; r++) {
      int term = 0;
      for(j=0; j<NIED2_NBITS; j++) {
        term = 2*term + ci[r][j];
      }
      ns->cj[r][i_dim] = term;
    }

  }
}


static int nied2_init(void * state, unsigned int dimension)
{
  nied2_state_t * n_state = (nied2_state_t *) state;
  unsigned int i_dim;

  if(dimension < 1 || dimension > NIED2_MAX_DIMENSION) return GSL_EINVAL;

  calculate_cj(n_state, dimension);

  for(i_dim=0; i_dim<dimension; i_dim++) n_state->nextq[i_dim] = 0;
  n_state->sequence_count = 0;

  return GSL_SUCCESS;
}


static int nied2_get(void * state, unsigned int dimension, double * v)
{
  static const double recip = 1.0/(double)(1U << NIED2_NBITS); /* 2^(-nbits) */
  nied2_state_t * n_state = (nied2_state_t *) state;
  int r;
  int c;
  unsigned int i_dim;

  /* Load the result from the saved state. */
  for(i_dim=0; i_dim<dimension; i_dim++) {
    v[i_dim] = n_state->nextq[i_dim] * recip;
  }

  /* Find the position of the least-significant zero in sequence_count.
   * This is the bit that changes in the Gray-code representation as
   * the count is advanced.
   */
  r = 0;
  c = n_state->sequence_count;
  while(1) {
    if((c % 2) == 1) {
      ++r;
      c /= 2;
    }
    else break;
  }

  if(r >= NIED2_NBITS) return GSL_EFAILED; /* FIXME: better error code here */

  /* Calculate the next state. */
  for(i_dim=0; i_dim<dimension; i_dim++) {
    n_state->nextq[i_dim] ^= n_state->cj[r][i_dim];
  }

  n_state->sequence_count++;

  return GSL_SUCCESS;
}