webmachinelearning / webnn-polyfill

🧠⚙️ Web Neural Network API polyfill based on TensorFlow.js
https://www.npmjs.com/package/@webmachinelearning/webnn-polyfill
Apache License 2.0
101 stars 18 forks source link

Enable WebNN polyfill API to support negative starts for slice op #152

Closed BruceDai closed 2 years ago

BruceDai commented 2 years ago

According to explanation of slice op, we should support negative starts as

  1. starts: a sequence of long. The starting indices to slice of the corresponding axes of the input shape. A negative index value is interpreted as counting back from the end. For example, the value -1
  it('slice with negative starts', function() {
    const inputShape = [3, 4, 5];
    const inputData = [
      1.3165863e+00,  4.1239005e-02,  4.6697399e-01,  -6.6145003e-02,
      -3.7128052e-01, -1.0660021e+00, 7.5784922e-01,  3.5759725e-02,
      1.9211160e+00,  -8.1603736e-01, 1.1800343e-01,  -1.8293047e+00,
      -2.1316205e-01, -3.6369815e-01, 6.4205879e-01,  7.1544610e-02,
      6.8498695e-01,  1.0001093e+00,  -5.6261641e-01, -7.3343945e-01,
      1.6827687e+00,  1.2653192e+00,  5.8872145e-01,  3.1535852e-01,
      3.5038650e-01,  3.5865438e-01,  -3.6469769e-01, -8.7751287e-01,
      2.7995768e-01,  -1.6042528e+00, 8.6336482e-01,  -1.7991974e+00,
      -6.8652731e-01, 1.3729302e-03,  -7.7775210e-01, 1.0199220e-01,
      4.2299256e-01,  1.1432177e-01,  -5.0116669e-02, 1.5525131e+00,
      -8.7060851e-01, 4.5739245e-01,  1.3543987e-01,  -1.5927458e-02,
      9.1792661e-01,  -4.5001405e-01, 1.9954188e-01,  -5.1338053e-01,
      -4.1026011e-01, -1.2718531e+00, 4.2538303e-01,  -1.5449624e-01,
      -3.4380481e-01, 7.8374326e-01,  1.7837452e+00,  9.6105379e-01,
      -4.8783422e-01, -9.4987392e-01, -8.8750905e-01, -9.8019439e-01,
    ];
    const starts = [-3, -4, -4];
    const sizes = [2, 3, 4];
    const expectedShape = [2, 3, 4];
    const expected = [
      4.1239005e-02,  4.6697399e-01,  -6.6145003e-02, -3.7128052e-01,
      7.5784922e-01,  3.5759725e-02,  1.9211160e+00,  -8.1603736e-01,
      -1.8293047e+00, -2.1316205e-01, -3.6369815e-01, 6.4205879e-01,
      1.2653192e+00,  5.8872145e-01,  3.1535852e-01,  3.5038650e-01,
      -3.6469769e-01, -8.7751287e-01, 2.7995768e-01,  -1.6042528e+00,
      -1.7991974e+00, -6.8652731e-01, 1.3729302e-03,  -7.7775210e-01,
    ];
    testSlice(
        inputShape, inputData, starts, sizes, undefined, expectedShape,
        expected);
  });

Error Error: Requested out of range element at -3,-4,-4. Buffer shape=3,4,5 happened.

BruceDai commented 2 years ago

The root cause is that negative index for location of TF.js tensor (tfjs-core/src/tensor.ts) is limited,

  /**
   * Returns the value in the buffer at the provided location.
   *
   * @param locs The location indices.
   *
   * @doc {heading: 'Tensors', subheading: 'Creation'}
   */
  get(...locs: number[]): SingleValueMap[D] {
    if (locs.length === 0) {
      locs = [0];
    }
    let i = 0;
    for (const loc of locs) {
      if (loc < 0 || loc >= this.shape[i]) {
        const msg = `Requested out of range element at ${locs}. ` +
            `  Buffer shape=${this.shape}`;
        throw new Error(msg);
      }
      i++;
    }
    let index = locs[locs.length - 1];
    for (let i = 0; i < locs.length - 1; ++i) {
      index += this.strides[i] * locs[i];
    }
    return this.values[index] as SingleValueMap[D];
  }

I will workaround in polyfill implementation to support it firstly.