<commit_before>import { typeOf } from '../arithmetic/base';
import { affineTransformations } from './types';


const V = affineTransformations;


const modZ = q => V.minus(q, V.floor(q));


const checkInteger = x => {
  if (!V.isInteger(x))
    throw new Error(`expected an integer, got ${x}`);
};


const checkShiftCoordinate = x => {
  if (!V.isRational(x))
    throw new Error(`expected a rational number, got ${x}`);
  if (!V.eq(x, modZ(x)))
    throw new Error(`expected a number in [0,1), got ${x}`);
};


const checkLinearPartOfOperator = (M, d) => {
  const [n, m] = V.shape(M);

  if (n != d || m != d)
    throw new Error(`expected a ${d}x${d} matrix, got ${M}`);

  M.forEach(row => row.forEach(checkInteger));

  const det = V.determinant(M);

  if (V.abs(det) != 1)
    throw new Error(
      `expected a unimodular matrix, got ${M} with determinant ${det}`
    );
};


const checkTranslationalPartOfOperator = (s, d) => {
  if (s.length != d)
    throw new Error(`expected a ${d}-dimensional vector, got ${s}`);

  s.forEach(checkShiftCoordinate);
};


const checkOperatorType = op => {
  const t = typeOf(op);

  if (t != 'Matrix' && t != 'AffineTransformation')
    throw new Error(`expected a Matrix or AffineTransformation, got ${op}`);
};


const checkOperator = (op, d) => {
  checkOperatorType(op);

  if (typeOf(op) == 'Matrix')
    checkLinearPartOfOperator(op, d);
  else {
    checkLinearPartOfOperator(op.linear, d);
    checkTranslationalPartOfOperator(op.shift, d);
  }
};


const operatorDimension = op =>
  (typeOf(op) == 'Matrix' ? op : op.linear).length;


const checkOperatorList = ops => {
  if (!ops.length)
    return;

  ops.forEach(checkOperatorType);

  const d = operatorDimension(ops[0]);
  ops.forEach(op => checkOperator(op, d));
};


const opModZ = op => {
  if (typeOf(op) == 'Matrix')
    return op;
  else
    return V.affineTransformation(op.linear, op.shift.map(modZ));
};


const fullOperatorList = gens => {
  const seen = {};
  gens.forEach(g => seen[g] = true);

  const ops = gens.slice();

  for (let i = 0; i < ops.length; ++i) {
    const A = ops[i];
    gens.forEach(B => {
      const AB = opModZ(V.times(A, V.inverse(B)));
      if (!seen[AB]) {
        ops.push(AB);
        seen[AB] = true;
      }
    });
  }

  return ops;
};


const checkGroup = ops => {
  const seen = {};
  ops.forEach(op => seen[op] = true);

  ops.forEach(A => {
    ops.forEach(B => {
      if (!seen[opModZ(V.times(A, V.inverse(B)))])
        throw new Error('operators do not form a group');
    })
  });
};


if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const check = (op, d) => {
    try {
      if (d == null) {
        checkOperatorList(op);
        checkGroup(op);
        console.log(`Operator list ${op} is okay`);
      }
      else {
        checkOperator(op, d);
        console.log(`Operator ${op} is okay`);
      }
    } catch(ex) {
      console.log(ex);
    }
  };

  const M = [[1,1,0],[0,1,5],[0,0,-1]];

  check(M, 3);
  check(V.affineTransformation(M, V.div([1,2,3], 3)), 3);

  check([1,1,0], 3);
  check([[1,1,0],[0,1,5]], 3);
  check([[1,1,0],[0,1,5],[0,0.2,1]], 3);
  check([[1,1,0],[0,1,5],[0,0,-2]], 3);
  check(V.affineTransformation(M, [V.div(5,3),0,1]), 3);

  check([ [[1,1],[3,4]], [[1,0],[1,1],[1,3]] ]);
  const ops = [
    [[-1,0],[0,1]],
    V.affineTransformation([[1,0],[0,1]], V.div([1,1], 2))
  ];
  check(ops);
  check(fullOperatorList(ops));
}
<commit_msg>add primitive cell computation for spacegroups<commit_after>import { typeOf } from '../arithmetic/base';
import { rationalMethods, rationals } from '../arithmetic/types';
import * as mats from '../arithmetic/matrices';

import { affineTransformations } from './types';

const X = rationalMethods.register(
  mats.methods(rationals, ['Integer', 'LongInt', 'Fraction'], false)
).ops();

const V = affineTransformations;


const modZ = q => V.minus(q, V.floor(q));


const isIdentity = M => {
  for (let i = 0; i < M.length; ++i) {
    const row = M[i];
    for (let j = 0; j < row.length; ++j) {
      if (V.ne(row[j], 0 + (i == j)))
        return false;
    }
  }

  return true;
};


const checkInteger = x => {
  if (!V.isInteger(x))
    throw new Error(`expected an integer, got ${x}`);
};


const checkShiftCoordinate = x => {
  if (!V.isRational(x))
    throw new Error(`expected a rational number, got ${x}`);
  if (!V.eq(x, modZ(x)))
    throw new Error(`expected a number in [0,1), got ${x}`);
};


const checkLinearPartOfOperator = (M, d) => {
  const [n, m] = V.shape(M);

  if (n != d || m != d)
    throw new Error(`expected a ${d}x${d} matrix, got ${M}`);

  M.forEach(row => row.forEach(checkInteger));

  const det = V.determinant(M);

  if (V.abs(det) != 1)
    throw new Error(
      `expected a unimodular matrix, got ${M} with determinant ${det}`
    );
};


const checkTranslationalPartOfOperator = (s, d) => {
  if (s.length != d)
    throw new Error(`expected a ${d}-dimensional vector, got ${s}`);

  s.forEach(checkShiftCoordinate);
};


const checkOperatorType = op => {
  const t = typeOf(op);

  if (t != 'Matrix' && t != 'AffineTransformation')
    throw new Error(`expected a Matrix or AffineTransformation, got ${op}`);
};


const checkOperator = (op, d) => {
  checkOperatorType(op);

  if (typeOf(op) == 'Matrix')
    checkLinearPartOfOperator(op, d);
  else {
    checkLinearPartOfOperator(op.linear, d);
    checkTranslationalPartOfOperator(op.shift, d);
  }
};


const operatorDimension = op =>
  (typeOf(op) == 'Matrix' ? op : op.linear).length;


const checkOperatorList = ops => {
  if (!ops.length)
    return;

  ops.forEach(checkOperatorType);

  const d = operatorDimension(ops[0]);
  ops.forEach(op => checkOperator(op, d));
};


const opModZ = op => {
  if (typeOf(op) == 'Matrix')
    return op;
  else
    return V.affineTransformation(op.linear, op.shift.map(modZ));
};


const fullOperatorList = gens => {
  const seen = {};
  gens.forEach(g => seen[g] = true);

  const ops = gens.slice();

  for (let i = 0; i < ops.length; ++i) {
    const A = ops[i];
    gens.forEach(B => {
      const AB = opModZ(V.times(A, V.inverse(B)));
      if (!seen[AB]) {
        ops.push(AB);
        seen[AB] = true;
      }
    });
  }

  return ops;
};


const checkGroup = ops => {
  const seen = {};
  ops.forEach(op => seen[op] = true);

  ops.forEach(A => {
    ops.forEach(B => {
      if (!seen[opModZ(V.times(A, V.inverse(B)))])
        throw new Error('operators do not form a group');
    })
  });
};


const primitiveCell = ops => {
  const d  = operatorDimension(ops[0]);
  const vs = ops
    .filter(op => typeOf(op) == 'AffineTransformation')
    .filter(op => isIdentity(op.linear))
    .map(op => op.shift);

  const B = V.identityMatrix(d).concat(vs);

  return X.triangulation(B).R.slice(0, d);
};


if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const check = (op, d) => {
    try {
      if (d == null) {
        checkOperatorList(op);
        checkGroup(op);
        console.log(`Operator list ${op} is okay`);
      }
      else {
        checkOperator(op, d);
        console.log(`Operator ${op} is okay`);
      }
    } catch(ex) {
      console.log(ex);
    }
  };

  const M = [[1,1,0],[0,1,5],[0,0,-1]];

  check(M, 3);
  check(V.affineTransformation(M, V.div([1,2,3], 3)), 3);

  check([1,1,0], 3);
  check([[1,1,0],[0,1,5]], 3);
  check([[1,1,0],[0,1,5],[0,0.2,1]], 3);
  check([[1,1,0],[0,1,5],[0,0,-2]], 3);
  check(V.affineTransformation(M, [V.div(5,3),0,1]), 3);

  check([ [[1,1],[3,4]], [[1,0],[1,1],[1,3]] ]);
  const ops = [
    [[-1,0],[0,1]],
    V.affineTransformation([[1,0],[0,1]], V.div([1,1], 2))
  ];
  check(ops);
  check(fullOperatorList(ops));

  console.log(isIdentity([[1,0,0],[0,1,0],[0,0,1]]));
  console.log(isIdentity([[1,0,0],[0,1,-1],[0,0,1]]));
  console.log(`${primitiveCell(fullOperatorList(ops))}`);
}

{'fraction_non_alphanumeric': 0.21150971599402094, 'fraction_numerical': 0.022296960637767812, 'mean_word_length': 3.1883150756390193, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5079389', 'n_tokens_mistral': 3134, 'n_tokens_neox': 3013, 'n_words': 666}