gpujs / gpu.js

GPU Accelerated JavaScript
https://gpu.rocks
MIT License
15.04k stars 646 forks source link

Shape indices should match typical array/tensor library shape for flattened array "input(...)" #771

Open callumhay opened 1 year ago

callumhay commented 1 year ago

Alt Text

What is wrong?

Currently the shape of a flattened array is in col, row order instead of row, col order. This doesn't match the shape of typical array libraries especially those in machine learning (scijs, numpy, tensorflow, etc.).

Where does it happen?

When using flattened arrays via input(...) / new Input(...)

How do we replicate the issue?

const firstArr = ndarray(new Uint8Array(20*10).fill(0), [20, 10]);
const secondArr = new Array(10).fill(0);
for (let i = 0; i < 10; i++) { secondArr[i] = i+1; }
for (let i = 0; i < 20; i++) { firstArr.set(i,0,i+1); }
const kernel = gpu.createKernel(function (a, b) {
      // Sum the product of the b vector for each row of a
      let result = 0;
      for (let i = 0; i < 10; i++) {
        result += a[this.thread.x][i]*b[i];
      }
      return result;
}, {...settings, output: [20], returnType: 'Float'});
kernel(input(firstArr.data, firstArr.shape), secondArr);

How important is this (1-5)?

3

Expected behavior (i.e. solution)

The resulting array for the example above should look like

Float32Array(20) [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]

but instead it looks like this:

Float32Array(20) [ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]

This is because the shape is being interpreted as the reverse of what it typically is in pretty much all existing array/tensor libraries.

In other words, a stop gap solution would be to change the last line in the above example to

kernel(input(firstArr.data, [...firstArr.shape].reverse()), secondArr);

This should probably not be the library user's problem though.