All Downloads are FREE. Search and download functionalities are using the official Maven repository.

umontreal.iro.lecuyer.hups.DigitalNet Maven / Gradle / Ivy

Go to download

SSJ is a Java library for stochastic simulation, developed under the direction of Pierre L'Ecuyer, in the Département d'Informatique et de Recherche Opérationnelle (DIRO), at the Université de Montréal. It provides facilities for generating uniform and nonuniform random variates, computing different measures related to probability distributions, performing goodness-of-fit tests, applying quasi-Monte Carlo methods, collecting (elementary) statistics, and programming discrete-event simulations with both events and processes.

The newest version!


/*
 * Class:        DigitalNet
 * Description:  
 * Environment:  Java
 * Software:     SSJ 
 * Copyright (C) 2001  Pierre L'Ecuyer and Université de Montréal
 * Organization: DIRO, Université de Montréal
 * @author       
 * @since

 * SSJ is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License (GPL) as published by the
 * Free Software Foundation, either version 3 of the License, or
 * any later version.

 * SSJ is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * A copy of the GNU General Public License is available at
   GPL licence site.
 */

package umontreal.iro.lecuyer.hups;

import umontreal.iro.lecuyer.util.PrintfFormat;
import umontreal.iro.lecuyer.rng.*;


/**
 * This class provides the basic structures for storing and
 * manipulating linear digital nets in base b, for an arbitrary
 * base b >= 2.  We recall that a net contains n = bk points in
 * s dimensions, where the ith point 
 * ui,
 * for 
 * i = 0,..., bk - 1, is defined as follows:
 * 
*
* * * * * * * * * * * * * * * * * * *
i     =     r=0k-1ai, rbr, *  
(ui, j, 1 ui, j, 2 …)T     =     Cj (ai, 0 ai, 1 … ai, k-1)T, *  
ui, j     =     r=1ui, j, rb-r, *  
ui     =     (ui, 0, …, ui, s-1). *  
*
* * In our implementation, the matrices * Cj are r×k, * so the expansion of ui, j is truncated to its first r terms. * The points are stored implicitly by storing the generator matrices * * Cj in a large two-dimensional array of integers, * with srk elements. * *

* The points * ui are enumerated using the Gray code * technique. * With this technique, the b-ary representation of i, * * ai = (ai, 0,..., ai, k-1), is replaced by a Gray code representation of i, * * gi = (gi, 0,..., gi, k-1). The Gray code * gi * used here is defined by * gi, k-1 = ai, k-1 and * * * gi, ν = (ai, ν - ai, ν+1)mod b for * ν = 0,..., k - 2. * It has the property that * * gi = (gi, 0,..., gi, k-1) and * * gi+1 = (gi+1, 0,..., gi+1, k-1) * differ only in the position of the smallest index * ν such that * * ai, ν < b - 1, and we have * gi+1, ν = (gi, ν +1)mod b * in that position. * *

* This Gray code representation permits a more efficient enumeration * of the points by the iterators. * It changes the order in which the points * ui are enumerated, * but the first bm points remain the same for every integer m. * The ith point of the sequence with the Gray enumeration * is the i'th point of the original enumeration, where i' is the * integer whose b-ary representation * ai' is given by the Gray * code * gi. * To enumerate all the points successively, we never need to compute * the Gray codes explicitly. * It suffices to know the position ν of the Gray code digit * that changes at each step, and this can be found quickly from the * b-ary representation * ai. * The digits of each coordinate j of the current point can be updated by * adding column ν of the generator matrix * Cj to the old digits, * modulo b. * *

* One should avoid using the method {@link #getCoordinate getCoordinate}(i, j) * for arbitrary values of i and j, because this is much * slower than using an iterator to access successive coordinates. * *

* Digital nets can be randomized in various ways. * Several types of randomizations specialized for nets are implemented * directly in this class. * *

* A simple but important randomization is the random digital shift * in base b, defined as follows: replace each digit * ui, j, ν in the third equation above * by * (ui, j, ν + dj, ν)mod b, * where the dj, ν's are i.i.d. uniform over * {0,..., b - 1}. * This is equivalent to applying a single random shift to all the points * in a formal series representation of their coordinates. * In practice, the digital shift is truncated to w digits, * for some integer w >= r. * Applying a digital shift does not change the equidistribution * and (t, m, s)-net properties of a point set. * Moreover, with the random shift, each point is uniformly distributed over * the unit hypercube (but the points are not independent, of course). * *

* A second class of randomizations specialized for digital nets * are the linear matrix scrambles, which multiply the matrices * * Cj * by a random invertible matrix * Mj, modulo b. * There are several variants, depending on how * Mj is generated, and on * whether * Cj is multiplied on the left or on the right. * In our implementation, the linear matrix scrambles are incorporated directly * into the matrices * Cj, so they * do not slow down the enumeration of points. * Methods are available for applying * linear matrix scrambles and for removing these randomizations. * These methods generate the appropriate random numbers and make * the corresponding changes to the * Cj's. * A copy of the original * Cj's is maintained, so the point * set can be returned to its original unscrambled state at any time. * When a new linear matrix scramble is applied, it is always applied to * the original generator matrices. * The method {@link #resetGeneratorMatrices resetGeneratorMatrices} removes the current matrix * scramble by resetting the generator matrices to their original state. * On the other hand, the method {@link #eraseOriginalGeneratorMatrices eraseOriginalGeneratorMatrices} * replaces the original generator matrices by the current * ones, making the changes permanent. * This is useful if one wishes to apply two or more linear matrix * scrambles on top of each other. * *

* Linear matrix scrambles are usually combined with a random digital shift; * this combination is called an affine matrix scramble. * These two randomizations are applied via separate methods. * The linear matrix scrambles are incorporated into the matrices * Cj * whereas the digital random shift is stored and applied separately, * independently of the other scramblings. * *

* Applying a digital shift or a linear matrix scramble to a digital net * invalidates all iterators for that randomized point, * because each iterator uses a * cached copy of the current point, which is updated only when * the current point index of that iterator changes, and the update also * depends on the cached copy of the previous point. * After applying any kind of scrambling, the iterators must be * reinitialized to the initial point by invoking * {@link PointSetIterator#resetCurPointIndex resetCurPointIndex} * or reinstantiated by the {@link #iterator iterator} method * (this is not done automatically). * */ public class DigitalNet extends PointSet { // Variables to be initialized by the constructor subclasses. protected int b = 0; // Base. protected int numCols = 0; // The number of columns in each C_j. (= k) protected int numRows = 0; // The number of rows in each C_j. (= r) protected int outDigits = 0; // Number of output digits (= w) private int[][] originalMat; // Original gen. matrices without randomizat. protected int[][] genMat; // The current generator matrices. // genMat[j*numCols+c][l] contains column c // and row l of C_j. protected int[][] digitalShift; // The digital shift, initially zero (null). // Entry [j][l] is for dimension j, digit l, // for 0 <= l < outDigits. protected double normFactor; // To convert output to (0,1); 1/b^outDigits. protected double[] factor; // Lookup table in ascending order: factor[i] // = 1/b^{i+1} for 0 <= i < outDigits. // primes gives the first index in array FaureFactor // for the prime p. If primes[i] = p, then // FaureFactor[p][j] contains the Faure ordered factors of base p. private int[] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67}; // Factors on the diagonal corresponding to base b = prime[i] ordered by // increasing Bounds. private int[][] FaureFactor = {{1}, {1, 2}, {2, 3, 1, 4}, { 2, 3, 4, 5, 1, 6}, {3, 4, 7, 8, 2, 5, 6, 9, 1, 10}, { 5, 8, 3, 4, 9, 10, 2, 6, 7, 11, 1, 12}, { 5, 7, 10, 12, 3, 6, 11, 14, 4, 13, 2, 8, 9, 15, 1, 16}, { 7, 8, 11, 12, 4, 5, 14, 15, 3, 6, 13, 16, 2, 9, 10, 17, 1, 18}, { 5, 9, 14, 18, 7, 10, 13, 16, 4, 6, 17, 19, 3, 8, 15, 20, 2, 11, 12, 21, 1, 22}, { 8, 11, 18, 21, 12, 17, 9, 13, 16, 20, 5, 6, 23, 24, 4, 7, 22, 25, 3, 10, 19, 26, 2, 14, 15, 27, 1, 28}, { 12, 13, 18, 19, 11, 14, 17, 20, 7, 9, 22, 24, 4, 8, 23, 27, 5, 6, 25, 26, 3, 10, 21, 28, 2, 15, 16, 29, 1, 30}, { 8, 14, 23, 29, 10, 11, 26, 27, 13, 17, 20, 24, 7, 16, 21, 30, 5, 15, 22, 32, 6, 31, 4, 9, 28, 33, 3, 12, 25, 34, 2, 18, 19, 35, 1, 36}, { 16, 18, 23, 25, 11, 15, 26, 30, 12, 17, 24, 29, 9, 32, 13, 19, 22, 28, 6, 7, 34, 35, 5, 8, 33, 36, 4, 10, 31, 37, 3, 14, 27, 38, 2, 20, 21, 39, 1, 40}, { 12, 18, 25, 31, 9, 19, 24, 34, 8, 16, 27, 35, 10, 13, 30, 33, 15, 20, 23, 28, 5, 17, 26, 38, 6, 7, 36, 37, 4, 11, 32, 39, 3, 14, 29, 40, 2, 21, 22, 41, 1, 42}, { 13, 18, 29, 34, 11, 17, 30, 36, 10, 14, 33, 37, 7, 20, 27, 40, 9, 21, 26, 38, 15, 22, 25, 32, 6, 8, 39, 41, 5, 19, 28, 42, 4, 12, 35, 43, 3, 16, 31, 44, 2, 23, 24, 45, 1, 46}, { 14, 19, 34, 39, 23, 30, 12, 22, 31, 41, 8, 11, 20, 24, 29, 33, 42, 45, 10, 16, 37, 43, 7, 15, 38, 46, 17, 25, 28, 36, 5, 21, 32, 48, 6, 9, 44, 47, 4, 13, 40, 49, 3, 18, 35, 50, 2, 26, 27, 51, 1, 52}, { 25, 26, 33, 34, 18, 23, 36, 41, 14, 21, 38, 45, 24, 27, 32, 35, 11, 16, 43, 48, 9, 13, 46, 50, 8, 22, 37, 51, 7, 17, 42, 52, 19, 28, 31, 40, 6, 10, 49, 53, 5, 12, 47, 54, 4, 15, 44, 55, 3, 20, 39, 56, 2, 29, 30, 57, 1, 58}, { 22, 25, 36, 39, 17, 18, 43, 44, 24, 28, 33, 37, 13, 14, 47, 48, 16, 19, 42, 45, 9, 27, 34, 52, 8, 23, 38, 53, 11, 50, 7, 26, 35, 54, 21, 29, 32, 40, 6, 10, 51, 55, 5, 12, 49, 56, 4, 15, 46, 57, 3, 20, 41, 58, 2, 30, 31, 59, 1, 60}, { 18, 26, 41, 49, 14, 24, 43, 53, 12, 28, 39, 55, 29, 30, 37, 38, 10, 20, 47, 57, 16, 21, 46, 51, 8, 25, 42, 59, 13, 31, 36, 54, 9, 15, 52, 58, 7, 19, 48, 60, 23, 32, 35, 44, 5, 27, 40, 62, 6, 11, 56, 61, 4, 17, 50, 63, 3, 22, 45, 64, 2, 33, 34, 65, 1, 66} }; public double getCoordinate (int i, int j) { // convert i to Gray representation, put digits in gdigit[]. int l, c, sum; int[] bdigit = new int[numCols]; int[] gdigit = new int[numCols]; int idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit); double result = 0; if (digitalShift != null && dimShift < j) addRandomShift (dimShift, j, shiftStream); for (l = 0; l < outDigits; l++) { if (digitalShift == null) sum = 0; else sum = digitalShift[j][l]; if (l < numRows) for (c = 0; c < idigits; c++) sum += genMat[j*numCols+c][l] * gdigit[c]; result += (sum % b) * factor[l]; } if (digitalShift != null) result += EpsilonHalf; return result; } public PointSetIterator iterator() { return new DigitalNetIterator(); } /** * Empty constructor. * */ public DigitalNet () { } /** * Returns ui, j, the coordinate j of point i, the points * being enumerated in the standard order (no Gray code). * * @param i point index * * @param j coordinate index * * @return the value of ui, j * */ public double getCoordinateNoGray (int i, int j) { // convert i to b-ary representation, put digits in bdigit[]. int l, c, sum; int[] bdigit = new int[numCols]; int idigits = 0; for (c = 0; i > 0; c++) { idigits++; bdigit[c] = i % b; i = i / b; } if (digitalShift != null && dimShift < j) addRandomShift (dimShift, j, shiftStream); double result = 0; for (l = 0; l < outDigits; l++) { if (digitalShift == null) sum = 0; else sum = digitalShift[j][l]; if (l < numRows) for (c = 0; c < idigits; c++) sum += genMat[j*numCols+c][l] * bdigit[c]; result += (sum % b) * factor[l]; } if (digitalShift != null) result += EpsilonHalf; return result; } /** * This iterator does not use the Gray code. Thus the points are enumerated * in the order of their first coordinate before randomization. * */ public PointSetIterator iteratorNoGray() { return new DigitalNetIteratorNoGray(); } /** * Adds a random digital shift to all the points of the point set, * using stream stream to generate the random numbers. * For each coordinate j from d1 to d2-1, * the shift vector * (dj, 0,..., dj, k-1) * is generated uniformly over * {0,..., b - 1}k and added modulo b to * the digits of all the points. * After adding a digital shift, all iterators must be reconstructed or * reset to zero. * * @param stream random number stream used to generate uniforms * * */ public void addRandomShift (int d1, int d2, RandomStream stream) { if (null == stream) throw new IllegalArgumentException ( PrintfFormat.NEWLINE + " Calling addRandomShift with null stream"); if (0 == d2) d2 = Math.max (1, dim); if (digitalShift == null) { digitalShift = new int[d2][outDigits]; capacityShift = d2; } else if (d2 > capacityShift) { int d3 = Math.max (4, capacityShift); while (d2 > d3) d3 *= 2; int[][] temp = new int[d3][outDigits]; capacityShift = d3; for (int i = 0; i < d1; i++) for (int j = 0; j < outDigits; j++) temp[i][j] = digitalShift[i][j]; digitalShift = temp; } for (int i = d1; i < d2; i++) for (int j = 0; j < outDigits; j++) digitalShift[i][j] = stream.nextInt (0, b - 1); dimShift = d2; shiftStream = stream; } /** * Same as {@link #addRandomShift addRandomShift}(0, dim, stream), * where dim is the dimension of the digital net. * * @param stream random number stream used to generate uniforms * * */ public void addRandomShift (RandomStream stream) { addRandomShift (0, dim, stream); } public void clearRandomShift() { super.clearRandomShift(); digitalShift = null; } public String toString() { StringBuffer sb = new StringBuffer (100); if (b > 0) { sb.append ("Base = "); sb.append (b); sb.append (PrintfFormat.NEWLINE); } sb.append ("Num cols = "); sb.append (numCols); sb.append (PrintfFormat.NEWLINE + "Num rows = "); sb.append (numRows); sb.append (PrintfFormat.NEWLINE + "outDigits = "); sb.append (outDigits); return sb.toString(); } // Print matrices M for dimensions 0 to N-1. private void printMat (int N, int[][][] A, String name) { for (int i = 0; i < N; i++) { System.out.println ("-------------------------------------" + PrintfFormat.NEWLINE + name + " dim = " + i); int l, c; // row l, column c, dimension i for A[i][l][c]. for (l = 0; l < numRows; l++) { for (c = 0; c < numCols; c++) { System.out.print (A[i][l][c] + " "); } System.out.println (""); } } System.out.println (""); } // Print matrix M private void printMat0 (int[][] A, String name) { System.out.println ("-------------------------------------" + PrintfFormat.NEWLINE + name); int l, c; // row l, column c for A[l][c]. for (l = 0; l < numCols; l++) { for (c = 0; c < numCols; c++) { System.out.print (A[l][c] + " "); } System.out.println (""); } System.out.println (""); } // Left-multiplies lower-triangular matrix Mj by original C_j, // where original C_j is in originalMat and result is in genMat. // This implementation is safe only if (numRows*(b-1)^2) is a valid int. private void leftMultiplyMat (int j, int[][] Mj) { int l, c, i, sum; // Dimension j, row l, column c for new C_j. for (l = 0; l < numRows ; l++) { for (c = 0; c < numCols; c++) { // Multiply row l of M_j by column c of C_j. sum = 0; for (i = 0; i <= l; i++) sum += Mj[l][i] * originalMat[j*numCols+c][i]; genMat[j*numCols+c][l] = sum % b; } } } // Left-multiplies diagonal matrix Mj by original C_j, // where original C_j is in originalMat and result is in genMat. // This implementation is safe only if (numRows*(b-1)^2) is a valid int. private void leftMultiplyMatDiag (int j, int[][] Mj) { int l, c, sum; // Dimension j, row l, column c for new C_j. for (l = 0; l < numRows ; l++) { for (c = 0; c < numCols; c++) { // Multiply row l of M_j by column c of C_j. sum = Mj[l][l] * originalMat[j*numCols+c][l]; genMat[j*numCols+c][l] = sum % b; } } } // Right-multiplies original C_j by upper-triangular matrix Mj, // where original C_j is in originalMat and result is in genMat. // This implementation is safe only if (numCols*(b-1)^2) is a valid int. private void rightMultiplyMat (int j, int[][] Mj) { int l, c, i, sum; // Dimension j, row l, column c for new C_j. for (l = 0; l < numRows ; l++) { for (c = 0; c < numCols; c++) { // Multiply row l of C_j by column c of M_j. sum = 0; for (i = 0; i <= c; i++) sum += originalMat[j*numCols+i][l] * Mj[i][c]; genMat[j*numCols+c][l] = sum % b; } } } private int getFaureIndex (String method, int sb, int flag) { // Check for errors in ...FaurePermut. Returns the index ib of the // base b in primes, i.e. b = primes[ib]. if (sb >= b) throw new IllegalArgumentException (PrintfFormat.NEWLINE + " sb >= base in " + method); if (sb < 1) throw new IllegalArgumentException (PrintfFormat.NEWLINE + " sb = 0 in " + method); if ((flag > 2) || (flag < 0)) throw new IllegalArgumentException ( PrintfFormat.NEWLINE + " lowerFlag not in {0, 1, 2} in " + method); // Find index ib of base in array primes int ib = 0; while ((ib < primes.length) && (primes[ib] < b)) ib++; if (ib >= primes.length) throw new IllegalArgumentException ("base too large in " + method); if (b != primes[ib]) throw new IllegalArgumentException ( "Faure factors are not implemented for this base in " + method); return ib; } /** * Applies a linear scramble by multiplying each * Cj on the left * by a w×w nonsingular lower-triangular matrix * Mj, * as suggested by Matoušek and implemented * by Hong and Hickernell. The diagonal entries of * each matrix * Mj are generated uniformly over * * {1,..., b - 1}, the entries below the diagonal are generated uniformly * over * {0,..., b - 1}, and all these entries are generated independently. * This means that in base b = 2, all diagonal elements are equal to 1. * * @param stream random number stream used to generate the randomness * * */ public void leftMatrixScramble (RandomStream stream) { int j, l, c; // dimension j, row l, column c. // If genMat contains the original gen. matrices, copy to originalMat. if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Constructs the lower-triangular scrambling matrices M_j, r by r. int[][][] scrambleMat = new int[dim][numRows][numRows]; for (j = 0 ; j < dim; j++) { for (l = 0; l < numRows; l++) { for (c = 0; c < numRows; c++) { if (c == l) // No zero on the diagonal. scrambleMat[j][l][c] = stream.nextInt(1, b - 1) ; else if (c < l) scrambleMat[j][l][c] = stream.nextInt(0, b - 1); else scrambleMat[j][l][c] = 0; // Zeros above the diagonal; } } } // Multiply M_j by the generator matrix C_j for each j. for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]); } /** * Similar to {@link #leftMatrixScramble leftMatrixScramble} except that all the * off-diagonal elements of the * Mj are 0. * * @param stream random number stream used to generate the randomness * * */ public void leftMatrixScrambleDiag (RandomStream stream) { int j, l, c; // dimension j, row l, column c. // If genMat contains the original gen. matrices, copy to originalMat. if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Constructs the diagonal scrambling matrices M_j, r by r. int[][][] scrambleMat = new int[dim][numRows][numRows]; for (j = 0 ; j < dim; j++) { for (l = 0; l < numRows; l++) { for (c = 0; c < numRows; c++) { if (c == l) // No zero on the diagonal. scrambleMat[j][l][c] = stream.nextInt(1, b - 1) ; else scrambleMat[j][l][c] = 0; // Diagonal matrix; } } } // Multiply M_j by the generator matrix C_j for each j. for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]); } private void LMSFaurePermut (String method, RandomStream stream, int sb, int lowerFlag) { /* If \texttt{lowerFlag = 2}, the off-diagonal elements below the diagonal of $\mathbf{M}_j$ are chosen as in \method{leftMatrixScramble}{}. If \texttt{lowerFlag = 1}, the off-diagonal elements below the diagonal of $\mathbf{M}_j$ are also chosen from the restricted set. If \texttt{lowerFlag = 0}, the off-diagonal elements of $\mathbf{M}_j$ are all 0. */ int ib = getFaureIndex (method, sb, lowerFlag); // If genMat contains the original gen. matrices, copy to originalMat. if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Constructs the lower-triangular scrambling matrices M_j, r by r. int jb; int j, l, c; // dimension j, row l, column c. int[][][] scrambleMat = new int[dim][numRows][numRows]; for (j = 0 ; j < dim; j++) { for (l = 0; l < numRows; l++) { for (c = 0; c < numRows; c++) { if (c == l) { jb = stream.nextInt(0, sb - 1); scrambleMat[j][l][c] = FaureFactor[ib][jb]; } else if (c < l) { if (lowerFlag == 2) { scrambleMat[j][l][c] = stream.nextInt(0, b - 1); } else if (lowerFlag == 1) { jb = stream.nextInt(0, sb - 1); scrambleMat[j][l][c] = FaureFactor[ib][jb]; } else { // lowerFlag == 0 scrambleMat[j][l][c] = 0; } } else scrambleMat[j][l][c] = 0; // Zeros above the diagonal; } } } // printMat (dim, scrambleMat, method); // Multiply M_j by the generator matrix C_j for each j. if (lowerFlag == 0) for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]); else for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]); } /** * Similar to {@link #leftMatrixScramble leftMatrixScramble} except that the diagonal elements * of each matrix * Mj are chosen from a restricted set of the best * integers as calculated by Faure. They are generated * uniformly over the first sb elements of array F, where F is * made up of a permutation of the integers * [1..(b - 1)]. These integers are * sorted by increasing order of the upper bounds of the extreme discrepancy * for the given integer. * * @param stream random number stream used to generate the randomness * * @param sb Only the first sb elements of F are used * * */ public void leftMatrixScrambleFaurePermut (RandomStream stream, int sb) { LMSFaurePermut ("leftMatrixScrambleFaurePermut", stream, sb, 2); } /** * Similar to {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut} except that all * off-diagonal elements are 0. * * @param stream random number stream used to generate the randomness * * @param sb Only the first sb elements of F are used * * */ public void leftMatrixScrambleFaurePermutDiag (RandomStream stream, int sb) { LMSFaurePermut ("leftMatrixScrambleFaurePermutDiag", stream, sb, 0); } /** * Similar to {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut} except that the * elements under the diagonal are also * chosen from the same restricted set as the diagonal elements. * * @param stream random number stream used to generate the randomness * * @param sb Only the first sb elements of F are used * * */ public void leftMatrixScrambleFaurePermutAll (RandomStream stream, int sb) { LMSFaurePermut ("leftMatrixScrambleFaurePermutAll", stream, sb, 1); } /** * Applies the i-binomial matrix scramble proposed by Tezuka * . * This multiplies each * Cj on the left * by a w×w nonsingular lower-triangular matrix * Mj as in * {@link #leftMatrixScramble leftMatrixScramble}, but with the additional constraint that * all entries on any given diagonal or subdiagonal of * Mj are identical. * * @param stream random number stream used as generator of the randomness * * */ public void iBinomialMatrixScramble (RandomStream stream) { int j, l, c; // dimension j, row l, column c. int diag; // random entry on the diagonal; int col1; // random entries below the diagonal; // If genMat is original generator matrices, copy it to originalMat. if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Constructs the lower-triangular scrambling matrices M_j, r by r. int[][][] scrambleMat = new int[dim][numRows][numRows]; for (j = 0 ; j < dim; j++) { diag = stream.nextInt(1, b - 1); for (l = 0; l < numRows; l++) { // Single random nonzero element on the diagonal. scrambleMat[j][l][l] = diag; for (c = l+1; c < numRows; c++) scrambleMat[j][l][c] = 0; } for (l = 1; l < numRows; l++) { col1 = stream.nextInt(0, b - 1); for (c = 0; l+c < numRows; c++) scrambleMat[j][l+c][c] = col1; } } // printMat (dim, scrambleMat, "iBinomialMatrixScramble"); for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]); } private void iBMSFaurePermut (String method, RandomStream stream, int sb, int lowerFlag) { int ib = getFaureIndex (method, sb, lowerFlag); // If genMat is original generator matrices, copy it to originalMat. if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Constructs the lower-triangular scrambling matrices M_j, r by r. int j, l, c; // dimension j, row l, column c. int diag; // random entry on the diagonal; int col1; // random entries below the diagonal; int jb; int[][][] scrambleMat = new int[dim][numRows][numRows]; for (j = 0 ; j < dim; j++) { jb = stream.nextInt(0, sb - 1); diag = FaureFactor[ib][jb]; for (l = 0; l < numRows; l++) { // Single random nonzero element on the diagonal. scrambleMat[j][l][l] = diag; for (c = l+1; c < numRows; c++) scrambleMat[j][l][c] = 0; } for (l = 1; l < numRows; l++) { if (lowerFlag == 2) { col1 = stream.nextInt(0, b - 1); } else if (lowerFlag == 1) { jb = stream.nextInt(0, sb - 1); col1 = FaureFactor[ib][jb]; } else { // lowerFlag == 0 col1 = 0; } for (c = 0; l+c < numRows; c++) scrambleMat[j][l+c][c] = col1; } } // printMat (dim, scrambleMat, method); if (lowerFlag > 0) for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]); else for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]); } /** * Similar to {@link #iBinomialMatrixScramble iBinomialMatrixScramble} except that the diagonal * elements of each matrix * Mj are chosen as in * {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut}. * * @param stream random number stream used to generate the randomness * * @param sb Only the first sb elements of F are used * * */ public void iBinomialMatrixScrambleFaurePermut (RandomStream stream, int sb) { iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermut", stream, sb, 2); } /** * Similar to {@link #iBinomialMatrixScrambleFaurePermut iBinomialMatrixScrambleFaurePermut} except that all the * off-diagonal elements are 0. * * @param stream random number stream used to generate the randomness * * @param sb Only the first sb elements of F are used * * */ public void iBinomialMatrixScrambleFaurePermutDiag (RandomStream stream, int sb) { iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermutDiag", stream, sb, 0); } /** * Similar to {@link #iBinomialMatrixScrambleFaurePermut iBinomialMatrixScrambleFaurePermut} except that the * elements under the diagonal are also * chosen from the same restricted set as the diagonal elements. * * @param stream random number stream used to generate the randomness * * @param sb Only the first sb elements of F are used * * */ public void iBinomialMatrixScrambleFaurePermutAll (RandomStream stream, int sb) { iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermutAll", stream, sb, 1); } /** * Applies the striped matrix scramble proposed by Owen. * It multiplies each * Cj on the left * by a w×w nonsingular lower-triangular matrix * Mj as in * {@link #leftMatrixScramble leftMatrixScramble}, but with the additional constraint that * in any column, all entries below the diagonal are equal to the * diagonal entry, which is generated randomly over * {1,..., b - 1}. * Note that for b = 2, the matrices * Mj become deterministic, with * all entries on and below the diagonal equal to 1. * * @param stream random number stream used as generator of the randomness * * */ public void stripedMatrixScramble (RandomStream stream) { int j, l, c; // dimension j, row l, column c. int diag; // random entry on the diagonal; int col1; // random entries below the diagonal; // If genMat contains original gener. matrices, copy it to originalMat. if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Constructs the lower-triangular scrambling matrices M_j, r by r. int[][][] scrambleMat = new int[dim][numRows][numRows]; for (j = 0 ; j < dim; j++) { for (c = 0; c < numRows; c++) { diag = stream.nextInt (1, b - 1); // Random entry in this column. for (l = 0; l < c; l++) scrambleMat[j][l][c] = 0; for (l = c; l < numRows; l++) scrambleMat[j][l][c] = diag; } } // printMat (dim, scrambleMat, "stripedMatrixScramble"); for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]); } /** * Similar to {@link #stripedMatrixScramble stripedMatrixScramble} except that the * elements on and under the diagonal of each matrix * Mj are * chosen as in {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut}. * * @param stream random number stream used as generator of the randomness * * @param sb Only the first sb elements of F are used * * */ public void stripedMatrixScrambleFaurePermutAll (RandomStream stream, int sb) { int ib = getFaureIndex ("stripedMatrixScrambleFaurePermutAll", sb, 1); // If genMat contains original gener. matrices, copy it to originalMat. if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Constructs the lower-triangular scrambling matrices M_j, r by r. int j, l, c; // dimension j, row l, column c. int diag; // random entry on the diagonal; int col1; // random entries below the diagonal; int jb; int[][][] scrambleMat = new int[dim][numRows][numRows]; for (j = 0 ; j < dim; j++) { for (c = 0; c < numRows; c++) { jb = stream.nextInt(0, sb - 1); diag = FaureFactor[ib][jb]; // Random entry in this column. for (l = 0; l < c; l++) scrambleMat[j][l][c] = 0; for (l = c; l < numRows; l++) scrambleMat[j][l][c] = diag; } } // printMat (dim, scrambleMat, "stripedMatrixScrambleFaurePermutAll"); for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]); } /** * Applies a linear scramble by multiplying each * Cj on the right * by a single k×k nonsingular upper-triangular matrix * M, * as suggested by Faure and Tezuka. * The diagonal entries of the matrix * M are generated uniformly over * * {1,..., b - 1}, the entries above the diagonal are generated uniformly * over * {0,..., b - 1}, and all the entries are generated independently. * The effect of this scramble is only to change the order in which the * points are generated. If one computes the average value of a * function over all the points of a given digital net, or over * a number of points that is a power of the basis, then this * scramble makes no difference. * * @param stream random number stream used as generator of the randomness * * */ public void rightMatrixScramble (RandomStream stream) { int j, c, l, i, sum; // dimension j, row l, column c, of genMat. // SaveOriginalMat(); if (originalMat == null) { originalMat = genMat; genMat = new int[dim * numCols][numRows]; } // Generate an upper triangular matrix for Faure-Tezuka right-scramble. // Entry [l][c] is in row l, col c. int[][] scrambleMat = new int[numCols][numCols]; for (c = 0; c < numCols; c++) { for (l = 0; l < c; l++) scrambleMat[l][c] = stream.nextInt (0, b - 1); scrambleMat[c][c] = stream.nextInt (1, b - 1); for (l = c+1; l < numCols; l++) scrambleMat[l][c] = 0; } // printMat0 (scrambleMat, "rightMatrixScramble"); // Right-multiply the generator matrices by the scrambling matrix. for (j = 0 ; j < dim; j++) rightMultiplyMat (j, scrambleMat); } public void unrandomize() { resetGeneratorMatrices(); digitalShift = null; } /** * Restores the original generator matrices. * This removes the current linear matrix scrambles. * */ public void resetGeneratorMatrices() { if (originalMat != null) { genMat = originalMat; originalMat = null; } } /** * Erases the original generator matrices and replaces them by * the current ones. The current linear matrix scrambles thus become * permanent. This is useful if we want to apply several * scrambles in succession to a given digital net. * */ public void eraseOriginalGeneratorMatrices() { originalMat = null; } /** * Prints the generator matrices in standard form for dimensions 1 to s. * */ public void printGeneratorMatrices (int s) { // row l, column c, dimension j. for (int j = 0; j < s; j++) { System.out.println ("dim = " + (j+1) + PrintfFormat.NEWLINE); for (int l = 0; l < numRows; l++) { for (int c = 0; c < numCols; c++) System.out.print (genMat[j*numCols+c][l] + " "); System.out.println (""); } System.out.println ("----------------------------------"); } } // Computes the digital expansion of $i$ in base $b$, and the digits // of the Gray code of $i$. // These digits are placed in arrays \texttt{bary} and \texttt{gray}, // respectively, and the method returns the number of digits in the // expansion. The two arrays are assumed to be of sizes larger or // equal to this new number of digits. protected int intToDigitsGray (int b, int i, int numDigits, int[] bary, int[] gray) { if (i == 0) return 0; int idigits = 0; // Num. of digits in b-ary and Gray repres. int c; // convert i to b-ary representation, put digits in bary[]. for (c = 0; i > 0; c++) { idigits++; bary[c] = i % b; i = i / b; } // convert b-ary representation to Gray code. gray[idigits-1] = bary[idigits-1]; int diff; for (c = 0; c < idigits-1; c++) { diff = bary[c] - bary[c+1]; if (diff < 0) gray[c] = diff + b; else gray[c] = diff; } for (c = idigits; c < numDigits; c++) gray[c] = bary[c] = 0; return idigits; } // ************************************************************************ protected class DigitalNetIterator extends PointSet.DefaultPointSetIterator { protected int idigits; // Num. of digits in current code. protected int[] bdigit; // b-ary code of current point index. protected int[] gdigit; // Gray code of current point index. protected int dimS; // = dim except in the shift iterator // children where it is = dim + 1. protected int[] cachedCurPoint; // Digits of coords of the current point, // with digital shift already applied. // Digit l of coord j is at pos. [j*outDigits + l]. // Has one more dimension for the shift iterators. public DigitalNetIterator() { // EpsilonHalf = 0.5 / Math.pow ((double) b, (double) outDigits); bdigit = new int[numCols]; gdigit = new int[numCols]; dimS = dim; cachedCurPoint = new int[(dim + 1)* outDigits]; init(); // This call is important so that subclasses don't // automatically call 'resetCurPointIndex' at the time // of construction as this may cause a subtle // 'NullPointerException' } public void init() { // See constructor resetCurPointIndex(); } // We want to avoid generating 0 or 1 public double nextDouble() { return nextCoordinate() + EpsilonHalf; } public double nextCoordinate() { if (curPointIndex >= numPoints || curCoordIndex >= dimS) outOfBounds(); int start = outDigits * curCoordIndex++; double sum = 0; // Can always have up to outDigits digits, because of digital shift. for (int k = 0; k < outDigits; k++) sum += cachedCurPoint [start+k] * factor[k]; if (digitalShift != null) sum += EpsilonHalf; return sum; } public void resetCurPointIndex() { if (digitalShift == null) for (int i = 0; i < cachedCurPoint.length; i++) cachedCurPoint[i] = 0; else { if (dimShift < dimS) addRandomShift (dimShift, dimS, shiftStream); for (int j = 0; j < dimS; j++) for (int k = 0; k < outDigits; k++) cachedCurPoint[j*outDigits + k] = digitalShift[j][k]; } for (int i = 0; i < numCols; i++) bdigit[i] = 0; for (int i = 0; i < numCols; i++) gdigit[i] = 0; curPointIndex = 0; curCoordIndex = 0; idigits = 0; } public void setCurPointIndex (int i) { if (i == 0) { resetCurPointIndex(); return; } curPointIndex = i; curCoordIndex = 0; if (digitalShift != null && dimShift < dimS) addRandomShift (dimShift, dimS, shiftStream); // Digits of Gray code, used to reconstruct cachedCurPoint. idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit); int c, j, l, sum; for (j = 0; j < dimS; j++) { for (l = 0; l < outDigits; l++) { if (digitalShift == null) sum = 0; else sum = digitalShift[j][l]; if (l < numRows) for (c = 0; c < idigits; c++) sum += genMat[j*numCols+c][l] * gdigit[c]; cachedCurPoint [j*outDigits+l] = sum % b; } } } public int resetToNextPoint() { // incremental computation. curPointIndex++; curCoordIndex = 0; if (curPointIndex >= numPoints) return curPointIndex; // Update the digital expansion of i in base b, and find the // position of change in the Gray code. Set all digits == b-1 to 0 // and increase the first one after by 1. int pos; // Position of change in the Gray code. for (pos = 0; gdigit[pos] == b-1; pos++) gdigit[pos] = 0; gdigit[pos]++; // Update the cachedCurPoint by adding the column of the gener. // matrix that corresponds to the Gray code digit that has changed. // The digital shift is already incorporated in the cached point. int c, j, l; int lsup = numRows; // Max index l if (outDigits < numRows) lsup = outDigits; for (j = 0; j < dimS; j++) { for (l = 0; l < lsup; l++) { cachedCurPoint[j*outDigits + l] += genMat[j*numCols + pos][l]; cachedCurPoint[j * outDigits + l] %= b; } } return curPointIndex; } } // ************************************************************************ protected class DigitalNetIteratorNoGray extends DigitalNetIterator { public DigitalNetIteratorNoGray() { super(); } public void setCurPointIndex (int i) { if (i == 0) { resetCurPointIndex(); return; } curPointIndex = i; curCoordIndex = 0; if (dimShift < dimS) addRandomShift (dimShift, dimS, shiftStream); // Convert i to b-ary representation, put digits in bdigit. idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit); int c, j, l, sum; for (j = 0; j < dimS; j++) { for (l = 0; l < outDigits; l++) { if (digitalShift == null) sum = 0; else sum = digitalShift[j][l]; if (l < numRows) for (c = 0; c < idigits; c++) sum += genMat[j*numCols+c][l] * bdigit[c]; cachedCurPoint [j*outDigits+l] = sum % b; } } } public int resetToNextPoint() { curPointIndex++; curCoordIndex = 0; if (curPointIndex >= numPoints) return curPointIndex; // Find the position of change in the digits of curPointIndex in base // b. Set all digits = b-1 to 0; increase the first one after by 1. int pos; for (pos = 0; bdigit[pos] == b-1; pos++) bdigit[pos] = 0; bdigit[pos]++; // Update the digital expansion of curPointIndex in base b. // Update the cachedCurPoint by adding 1 unit at the digit pos. // If pos > 0, remove b-1 units in the positions < pos. Since // calculations are mod b, this is equivalent to adding 1 unit. // The digital shift is already incorporated in the cached point. int c, j, l; int lsup = numRows; // Max index l if (outDigits < numRows) lsup = outDigits; for (j = 0; j < dimS; j++) { for (l = 0; l < lsup; l++) { for (c = 0; c <= pos; c++) cachedCurPoint[j*outDigits + l] += genMat[j*numCols + c][l]; cachedCurPoint[j * outDigits + l] %= b; } } return curPointIndex; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy