Skip to content
Snippets Groups Projects
Commit 80d18f4a authored by David Sehnal's avatar David Sehnal
Browse files

tensors

parent 5b1d6b62
Branches
Tags
No related merge requests found
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import T from '../tensor'
import { Mat4 } from '../linear-algebra-3d'
describe('tensor', () => {
it('vector', () => {
const V = T.Vector(3);
const data = V.create();
V.set(data, 0, 1);
V.set(data, 1, 2);
V.set(data, 2, 3);
expect(data).toEqual(new Float64Array([1, 2, 3]));
expect(V.get(data, 0)).toEqual(1);
expect(V.get(data, 1)).toEqual(2);
expect(V.get(data, 2)).toEqual(3);
});
it('matrix cm', () => {
const M = T.ColumnMajorMatrix(3, 2, Int32Array);
const data = M.create()
// rows: [ [0, 1], [1, 2], [2, 3] ]
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 2; j++) {
M.set(data, i, j, i + j);
}
}
expect(data).toEqual(new Int32Array([0, 1, 2, 1, 2, 3]));
});
it('matrix rm', () => {
const M = T.RowMajorMatrix(3, 2, Int32Array);
const data = M.create();
// rows: [ [0, 1], [1, 2], [2, 3] ]
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 2; j++) {
M.set(data, i, j, i + j);
}
}
expect(data).toEqual(new Int32Array([0, 1, 1, 2, 2, 3]));
});
it('mat4 equiv', () => {
const M = T.ColumnMajorMatrix(4, 4);
const data = M.create();
const m = Mat4.zero();
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
const v = (i + 1) * (j + 2);
M.set(data, i, j, v);
Mat4.setValue(m, i, j, v);
}
}
for (let i = 0; i < 16; i++) expect(data[i]).toEqual(m[i]);
});
it('2d ij', () => {
const M = T.Space([3, 4], [0, 1]);
const data = M.create();
const exp = new Float64Array(3 * 4)
let o = 0;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 4; j++) {
M.set(data, i, j, o);
exp[o] = o;
o++;
}
}
expect(data).toEqual(exp);
});
it('2d ji', () => {
const M = T.Space([3, 4], [1, 0]);
const data = M.create();
const exp = new Float64Array(3 * 4)
let o = 0;
for (let j = 0; j < 4; j++) {
for (let i = 0; i < 3; i++) {
M.set(data, i, j, o);
exp[o] = o;
o++;
}
}
expect(data).toEqual(exp);
});
it('3d ijk', () => {
const M = T.Space([3, 4, 5], [0, 1, 2]);
const data = M.create();
const exp = new Float64Array(3 * 4 * 5)
let o = 0;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 4; j++) {
for (let k = 0; k < 5; k++) {
M.set(data, i, j, k, o);
exp[o] = o;
o++;
}
}
}
expect(data).toEqual(exp);
});
it('3d ikj', () => {
const M = T.Space([3, 3, 3], [0, 2, 1]);
const data = M.create();
const exp = new Float64Array(3 * 3 * 3)
let o = 0;
for (let i = 0; i < 3; i++) {
for (let k = 0; k < 3; k++) {
for (let j = 0; j < 3; j++) {
M.set(data, i, j, k, o);
exp[o] = o;
o++;
}
}
}
expect(data).toEqual(exp);
});
it('3d jik', () => {
const M = T.Space([3, 3, 3], [1, 0, 2]);
const data = M.create();
const exp = new Float64Array(3 * 3 * 3)
let o = 0;
for (let j = 0; j < 3; j++) {
for (let i = 0; i < 3; i++) {
for (let k = 0; k < 3; k++) {
M.set(data, i, j, k, o);
exp[o] = o;
o++;
}
}
}
expect(data).toEqual(exp);
});
it('3d jki', () => {
const M = T.Space([3, 3, 3], [1, 2, 0]);
const data = M.create();
const exp = new Float64Array(3 * 3 * 3)
let o = 0;
for (let j = 0; j < 3; j++) {
for (let k = 0; k < 3; k++) {
for (let i = 0; i < 3; i++) {
M.set(data, i, j, k, o);
exp[o] = o;
o++;
}
}
}
expect(data).toEqual(exp);
});
it('3d kij', () => {
const M = T.Space([3, 3, 3], [2, 0, 1]);
const data = M.create();
const exp = new Float64Array(3 * 3 * 3)
let o = 0;
for (let k = 0; k < 3; k++) {
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
M.set(data, i, j, k, o);
exp[o] = o;
o++;
}
}
}
expect(data).toEqual(exp);
});
it('3d kji', () => {
const M = T.Space([3, 3, 3], [2, 1, 0]);
const data = M.create();
const exp = new Float64Array(3 * 3 * 3)
let o = 0;
for (let k = 0; k < 3; k++) {
for (let j = 0; j < 3; j++) {
for (let i = 0; i < 3; i++) {
M.set(data, i, j, k, o);
exp[o] = o;
o++;
}
}
}
expect(data).toEqual(exp);
});
});
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
interface Tensor extends Array<number> { '@type': 'tensor' }
namespace Tensor {
export type ArrayCtor = { new (size: number): ArrayLike<number> }
export interface Space {
readonly dimensions: ReadonlyArray<number>,
readonly axisOrderSlowToFast: ReadonlyArray<number>,
create(array?: ArrayCtor): Tensor,
get(data: Tensor, ...coords: number[]): number
set(data: Tensor, ...coordsAndValue: number[]): number
}
interface Layout {
dimensions: number[],
// slowest to fastest changing
axisOrderSlowToFast: number[],
axisOrderFastToSlow: number[],
accessDimensions: number[],
// if not specified, use Float64Array
defaultCtor: ArrayCtor
}
function Layout(dimensions: number[], axisOrderSlowToFast: number[], ctor?: ArrayCtor): Layout {
// need to reverse the axis order for better access.
const axisOrderFastToSlow: number[] = [];
for (let i = 0; i < axisOrderSlowToFast.length; i++) axisOrderFastToSlow[i] = axisOrderSlowToFast[axisOrderSlowToFast.length - i - 1];
const accessDimensions = [1];
for (let i = 1; i < dimensions.length; i++) accessDimensions[i] = dimensions[axisOrderFastToSlow[i - 1]];
return { dimensions, axisOrderFastToSlow, axisOrderSlowToFast, accessDimensions, defaultCtor: ctor || Float64Array }
}
export function Space(dimensions: number[], axisOrderSlowToFast: number[], ctor?: ArrayCtor): Space {
const layout = Layout(dimensions, axisOrderSlowToFast, ctor);
const { get, set } = accessors(layout);
return { dimensions: [...dimensions], axisOrderSlowToFast: [...axisOrderSlowToFast], create: creator(layout), get, set };
}
export function Vector(d: number, ctor?: ArrayCtor) { return Space([d], [0], ctor); }
export function ColumnMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [1, 0], ctor); }
export function RowMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [0, 1], ctor); }
function accessors(layout: Layout): { get: Space['get'], set: Space['set'] } {
const { dimensions, axisOrderFastToSlow: ao } = layout;
switch (dimensions.length) {
case 1: return { get: (t, d) => t[d], set: (t, d, x) => t[d] = x };
case 2: {
// column major
if (ao[0] === 0 && ao[1] === 1) {
const rows = dimensions[0];
return { get: (t, i, j) => t[j * rows + i], set: (t, i, j, x) => t[j * rows + i] = x };
}
if (ao[0] === 1 && ao[1] === 0) {
const cols = dimensions[1];
return { get: (t, i, j) => t[i * cols + j], set: (t, i, j, x) => t[i * cols + j] = x };
}
throw new Error('bad axis order')
}
case 3: {
if (ao[0] === 0 && ao[1] === 1 && ao[2] === 2) { // 012 ijk
const u = dimensions[0], v = dimensions[1], uv = u * v;
return { get: (t, i, j, k) => t[i + j * u + k * uv], set: (t, i, j, k, x ) => t[i + j * u + k * uv] = x };
}
if (ao[0] === 0 && ao[1] === 2 && ao[2] === 1) { // 021 ikj
const u = dimensions[0], v = dimensions[2], uv = u * v;
return { get: (t, i, j, k) => t[i + k * u + j * uv], set: (t, i, j, k, x ) => t[i + k * u + j * uv] = x };
}
if (ao[0] === 1 && ao[1] === 0 && ao[2] === 2) { // 102 jik
const u = dimensions[1], v = dimensions[0], uv = u * v;
return { get: (t, i, j, k) => t[j + i * u + k * uv], set: (t, i, j, k, x ) => t[j + i * u + k * uv] = x };
}
if (ao[0] === 1 && ao[1] === 2 && ao[2] === 0) { // 120 jki
const u = dimensions[1], v = dimensions[2], uv = u * v;
return { get: (t, i, j, k) => t[j + k * u + i * uv], set: (t, i, j, k, x ) => t[j + k * u + i * uv] = x };
}
if (ao[0] === 2 && ao[1] === 0 && ao[2] === 1) { // 201 kij
const u = dimensions[2], v = dimensions[0], uv = u * v;
return { get: (t, i, j, k) => t[k + i * u + j * uv], set: (t, i, j, k, x ) => t[k + i * u + j * uv] = x };
}
if (ao[0] === 2 && ao[1] === 1 && ao[2] === 0) { // 210 kji
const u = dimensions[2], v = dimensions[1], uv = u * v;
return { get: (t, i, j, k) => t[k + j * u + i * uv], set: (t, i, j, k, x ) => t[k + j * u + i * uv] = x };
}
throw new Error('bad axis order')
}
default: return {
get: (t, ...c) => t[dataOffset(layout, c)],
set: (t, ...c) => t[dataOffset(layout, c)] = c[c.length - 1]
};
}
}
function creator(layout: Layout): Space['create'] {
const { dimensions: ds } = layout;
let size = 1;
for (let i = 0, _i = ds.length; i < _i; i++) size *= ds[i];
return ctor => new (ctor || layout.defaultCtor)(size) as Tensor;
}
function dataOffset(layout: Layout, coord: number[]) {
const { accessDimensions: acc, axisOrderFastToSlow: ao } = layout;
const d = acc.length - 1;
let o = acc[d] * coord[ao[d]];
for (let i = d - 1; i >= 0; i--) {
o = (o + coord[ao[i]]) * acc[i];
}
return o;
}
}
export default Tensor
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment