/*
* Redberry: symbolic tensor computations.
*
* Copyright (c) 2010-2012:
* Stanislav Poslavsky <stvlpos@mail.ru>
* Bolotin Dmitriy <bolotin.dmitriy@gmail.com>
*
* This file is part of Redberry.
*
* Redberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Redberry 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.
*
* You should have received a copy of the GNU General Public License
* along with Redberry. If not, see <http://www.gnu.org/licenses/>.
*/
package cc.redberry.core.tensor.random;
import cc.redberry.core.indices.IndicesUtils;
import cc.redberry.core.indices.IndexType;
import cc.redberry.core.indices.IndicesFactory;
import cc.redberry.core.indices.IndicesTypeStructure;
import cc.redberry.core.indices.SimpleIndices;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math.random.BitsStreamGenerator;
import org.apache.commons.math.random.Well44497b;
import cc.redberry.core.context.CC;
import cc.redberry.core.context.NameDescriptor;
import cc.redberry.core.tensor.Product;
import cc.redberry.core.tensor.SimpleTensor;
import cc.redberry.core.combinatorics.Symmetry;
public final class RandomProduct {
private static final int ALPHABET_SIZE = 26;
private final static byte[] TYPES = {0, 1, 2, 3};
private final BitsStreamGenerator random;
private final int nMax, nMin;
private final boolean withSymmetries;
private long seed;
private final int[] maxIndicesPerTensor;
private final int[] indicesCount;
private final int[] maxMinFreeIndicesCount;
private final int maxTensorCount;
private final int maxScalarsCount;
public RandomProduct(int nMax, int nMin,
int[] maxMinFreeIndicesCount, int[] indicesCount,
int[] maxIndicesPerTensor,
int maxTensorCount, int maxScalarsCount, boolean withSymmetries) {
this.nMax = nMax;
this.nMin = nMin;
this.withSymmetries = withSymmetries;
this.maxMinFreeIndicesCount = maxMinFreeIndicesCount;
this.indicesCount = indicesCount;
this.maxIndicesPerTensor = maxIndicesPerTensor;
this.maxTensorCount = maxTensorCount;
this.maxScalarsCount = maxScalarsCount;
random = new Well44497b();
reset();
}
public void reset() {
random.setSeed(seed = random.nextLong());
}
public void reset(long seed) {
random.setSeed(this.seed = seed);
}
public long getSeed() {
return seed;
}
public Product next() {
int i, j;
int[] minFree = new int[IndexType.TYPES_COUNT];
for (i = 0; i < IndexType.TYPES_COUNT; ++i)
minFree[i] = nextInt(random, maxMinFreeIndicesCount[i]);
int N = nMin + nextInt(random, nMax - nMin);
List<NameDescriptor> descriptors = new ArrayList<>();
int[] typesCount;
NameDescriptor nameDescriptor;
for (i = 0; i < N; ++i) {
typesCount = new int[IndexType.TYPES_COUNT];
for (j = 0; j < IndexType.TYPES_COUNT; ++j)
typesCount[j] = nextInt(random, maxIndicesPerTensor[j]);
IndicesTypeStructure typeStructure = new IndicesTypeStructure(TYPES, typesCount);
descriptors.add(nameDescriptor = new NameDescriptor(getName(i), typeStructure));
CC.getNameManager().mapNameDescriptor(descriptors.get(i));
if (withSymmetries)
addRandomSymmetries(nameDescriptor);
}
Product product = new Product();
IndicesProvider indicesProvider = new IndicesProvider(random, indicesCount, minFree);
SimpleIndices ind;
int scalars = 0;
while (!descriptors.isEmpty() && product.size() <= maxTensorCount) {
int descriptorIndex = nextInt(random, descriptors.size());
NameDescriptor nd = descriptors.get(descriptorIndex);
if (nd.getIndexTypeStructure().size() == 0)
if (scalars++ > maxScalarsCount) {
descriptors.remove(descriptorIndex);
continue;
}
ind = indicesProvider.next(nd);
if (ind == null) {
descriptors.remove(descriptorIndex);
continue;
}
product.add(new SimpleTensor(nd.getId(), ind));
}
return product;
}
public static String getName(int i) {
int second = i / ALPHABET_SIZE;
int first = i - second * ALPHABET_SIZE;
if (second == 0)
return new String(new char[]{(char) (0x41 + first)});
else
return new String(new char[]{(char) (0x40 + second), (char) (0x41 + first)});
}
public static int nextInt(BitsStreamGenerator generator, int n) {
if (n == 0)
return 0;
return generator.nextInt(n);
}
private static class IndicesProvider {
final static byte[] TYPES = {0, 1, 2, 3};
final BitsStreamGenerator random;
final int[][] indices;
final int[] lengths;
final int[] minLegth;
IndicesProvider(BitsStreamGenerator random, int[] indicesCounts, int[] minLegth) {
this.minLegth = minLegth;
indices = new int[IndexType.TYPES_COUNT][];
int i, j;
for (i = 0; i < TYPES.length; ++i) {
indices[i] = new int[indicesCounts[i] * 2];
for (j = 0; j < indicesCounts[i]; ++j) {
indices[i][j] = IndicesUtils.createIndex(j, (byte) i, false);
indices[i][indicesCounts[i] + j] = IndicesUtils.createIndex(j, (byte) i, true);
}
}
lengths = indicesCounts.clone();
for (i = 0; i < TYPES.length; ++i)
lengths[i] *= 2;
this.random = random;
}
private int next(byte type) {
if (lengths[type] == 0)
throw new IllegalStateException();
int n = nextInt(random, lengths[type]);
int index = indices[type][n];
if (n != lengths[type] - 1)
System.arraycopy(indices[type], n + 1, indices[type], n, lengths[type] - n - 1);
--lengths[type];
return index;
}
SimpleIndices next(NameDescriptor nd) {
IndicesTypeStructure typeStructure = nd.getIndexTypeStructure();
final int[] indices = new int[typeStructure.size()];
for (int i = 0; i < indices.length; ++i) {
if (lengths[typeStructure.get(i)] <= minLegth[typeStructure.get(i)])
return null;
indices[i] = next(typeStructure.get(i));
}
return IndicesFactory.createSimple(indices);
}
}
private void addRandomSymmetries(NameDescriptor descriptor) {
if (!descriptor.getSymmetries().isEmpty())
throw new RuntimeException("Symmetries already exists");
IndicesTypeStructure typeStructure = descriptor.getIndexTypeStructure();
int i;
for (byte type = 0; type < IndexType.TYPES_COUNT; ++type) {
IndicesTypeStructure.TypeData typeData = typeStructure.getTypeDatas(type);
if (typeData == null)
continue;
if (typeData.length == 0)//redundant
continue;
for (i = 0; i < random.nextInt(3); ++i)
descriptor.addSymmetry(type, new Symmetry(getRandomPermutation(typeData.length, random), false));
// try {
// descriptor.addSymmetry(type, new Symmetry(getRandomPermutation(typeData.length, random), random.nextBoolean()));
// } catch (InconsistentGeneratorsException exception) {
// }
}
}
public static int[] getRandomPermutation(final int dimension, BitsStreamGenerator generator) {
if (dimension == 0)
return new int[0];
int[] permutation = new int[dimension];
if (dimension == 1)
return permutation;
int i, r = generator.nextInt(1000);
//cycle permutation
if (r < 100) {
for (i = 0; i < dimension - 1; ++i)
permutation[i] = i + 1;
permutation[dimension - 1] = 0;
return permutation;
}
for (i = 1; i < dimension; ++i)
permutation[i] = i;
//else composition of transpositions
if (r < 700) {
int p1, p2;
final int tries = generator.nextInt(3) + 1;
for (i = 0; i < tries; ++i) {
while ((p1 = generator.nextInt(dimension)) == (p2 = generator.nextInt(dimension)));
swap(permutation, p1, p2);
}
}
//else identity
return permutation;
}
private static void swap(int[] a, int p1, int p2) {
int c = a[p1];
a[p1] = a[p2];
a[p2] = c;
}
}