Skip to content

Conversation

@BruceDai
Copy link
Contributor

@BruceDai BruceDai commented Nov 2, 2022

@fdwr This pr only has concat operation tests, this would make you review easily. I moved tests and test data onto resources/test_data/concat.json JSON file which will make maintain test smoothly, and I also addressed some feedbacks which you commented on #34287. Please help review it, thanks a lot.

@dontcallmedom @Honry @huningxin Please also take a review, thanks.

}
},
"inputsData": {
"float64Data": [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These inputsData and below expectedData were automatically generated by this scripts of WebNN-Baseline project.

graph = await builder.build({outputOperand});
await context.compute(graph, namedInputs, outputs);
}
assert_array_approx_equals_ulp(outputs.outputOperand, expectedShapeValue.data, PrecisionMetrics.concat.ULP[operandType], operandType);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] Hmm, why does this function name use snake case whereas nearly every other function and variable uses camelCase? (e.g. sizeOfShape, computeSync, loadTestData)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion function assert_array_approx_equals_ulp() was intended to add into testharness.js previously, so its' name followed like function assert_array_equals() of testharness.js.
Since assert_array_approx_equals_ulp() is especial for WebNN API tests, I'm OK to use camelCase style.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. So there was some external convention.

Copy link
Contributor Author

@BruceDai BruceDai Nov 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.
I go through some wpt tests of others features, there are common problems for variable/function name with snake case or camelCase ... :(
So what's your suggestion for this assertion function? Thanks.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I generally lean toward internal consistency with whatever code is immediately around it, but there's also the consistency argument of matching other "assert_" functions. I'm fine with whichever you decide - you answered my question either way. ✅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave this assert to be consistent with other "assert_" functions in testharness.js, thanks.

distance = actualBitwise - expectedBitwise;
distance = distance >= 0 ? distance : -distance;
assert_true(distance <= nulp,
`actual ${actual[i]} should be close enough to expected ${expected[i]} by the acceptable ${nulp} ULP distance, while they have ${distance} ULP distance`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Propose "while" -> "but".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.


// Refer to precision metrics on https://github.com/webmachinelearning/webnn/issues/265#issuecomment-1256242643
const PrecisionMetrics = {
concat: {ULP: {float32: 0, float16: 0}},
Copy link

@fdwr fdwr Nov 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thanks for the order update.

}
const expectedShapeValue = {shape: test.expected.shape, data: expectedData[expectedDataCategory]};
await testConcat(operandType, isSync, inputShapeValues, test.axis, expectedShapeValue);
}, `${test.name} / ${operandType} / ${deviceType} / ${executionType}`);
Copy link

@fdwr fdwr Nov 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ignorable] I notice you usually put a new line after most }'s, and so at first glance, it looked like a typo here, as I couldn't tell if the brace was in the wrong place for the for loop, not realizing it was a second parameter to the promise call. So new line after the },? (note I'm not familiar with Javascript common style conventions, but some I find easier to visually parse than others 🤷)

for (const shape of inputShapes) {
const size = sizeOfShape(shape);
inputShapeValues.push({shape, data: inputsData[inputDataCategory].slice(pos, pos + size)});
pos += size;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about position or offset? (I just like full words :))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

}

const json = loadJSON(file);
return JSON.parse(json.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this way we can have comments in the JSON.

Copy link
Contributor

@Honry Honry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM, thanks @BruceDai !

Copy link
Contributor Author

@BruceDai BruceDai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fdwr @Honry Thanks for your reviewing, I've updated PR to address your comments, please take another look, thanks.

graph = await builder.build({outputOperand});
await context.compute(graph, namedInputs, outputs);
}
assert_array_approx_equals_ulp(outputs.outputOperand, expectedShapeValue.data, PrecisionMetrics.concat.ULP[operandType], operandType);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave this assert to be consistent with other "assert_" functions in testharness.js, thanks.

distance = actualBitwise - expectedBitwise;
distance = distance >= 0 ? distance : -distance;
assert_true(distance <= nulp,
`actual ${actual[i]} should be close enough to expected ${expected[i]} by the acceptable ${nulp} ULP distance, while they have ${distance} ULP distance`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

for (const shape of inputShapes) {
const size = sizeOfShape(shape);
inputShapeValues.push({shape, data: inputsData[inputDataCategory].slice(pos, pos + size)});
pos += size;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

@BruceDai
Copy link
Contributor Author

Hi @huningxin, according to your suggestion on webmachinelearning/webnn-polyfill#172, I updated this PR by invoking sync functions and async functions clearly not using syncFlag flag, please have a review, thanks.

@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch from 7eefda2 to fe02442 Compare November 11, 2022 01:40
return [{outputOperand}, inputs, outputs];
};

testWebNNOperation('concat', concatTests(), buildGraph); No newline at end of file
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @fdwr, @Honry and @huningxin,
I refined code to reuse for later adding other operations tests. Now each test file named <operation-name>.https.any.js will have <operation-name>Tests(), buildGraph() two functions, and run test by invoking testWebNNOperation() function, please take another look, thanks.

}
tolerance++;
}
break;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fdwr I created this getPrecisonTolerance() function, and add detail implementation to get a ULP tolerance for these operations which need compute by IEPOE, please take another look, thanks.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be simpler to just include the function name in the table, right beside all the other values, rather than needing to fill in a switch statement (because then there are two separate places to look for values and update, a table and a code switch)? e.g.

function getGemmPrecisionTolerance(shapeA, options) {
  const shapeA = resources.inputA.shape;
  options = resources.options;
  ...
}

...

const PrecisionMetrics = {
    batchNormalization: {ULP: {float32: 6, float16: 6}},
...
    gemm: {ULP: {float32: getGemmPrecisionTolerance, float16: getGemmPrecisionTolerance}}
...
    maxPool2d: {ULP: {float32: 0, float16: 0}},
...
};

...

function getPrecisonTolerance(opName, metricType, tensorDataType, resources) {
   let tolerance;
    if (metricType === 'ULP') {
      tolerance = PrecisionMetrics[opName].ULP[tensorDataType];
    }
    else if (metricType === 'ATOL') {
      tolerance = PrecisionMetrics[opName].ATOL[operandType];
    }

    // If the tolerance is dynamic, then evaluate the function to get the value.
    if (x instanceof Function) {
      tolerance = tolerance(resources);
    }
    return tolerance;
}

(but I'm also fine with the current code)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch 2 times, most recently from 05e0375 to 9024b21 Compare November 16, 2022 15:59
* @param {Boolean} [options.bTranspose]
* @returns {Number} A tolerance number
*/
function getGemmPrecisionTolerance(shapeA, options) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason that we have to put these functions inside the testWebNNOperation? This would make code not easy to read.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Honry, these inner functions are only invoked by the implementation of testWebNNOperation() function, for this purpose, I define them inside, thanks.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, nested functions are harder to read and discern where the beginning and end are (even if only called by that one function).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestion, moved these functions outside.


const TypedArrayDict = {
float32: Float32Array,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(minor) We might as well fill in a few more now, given we know the other data types already (no reason to wait, right?).

int32: Int32Array,
uint32: Uint32Array,
int8: Int8Array,
uint8: Uint8Array,

(obviously float16 is harder and can come later, after deciding how to handle it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added these code, thanks.

* @param {Boolean} [options.bTranspose]
* @returns {Number} A tolerance number
*/
function getGemmPrecisionTolerance(shapeA, options) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, nested functions are harder to read and discern where the beginning and end are (even if only called by that one function).

}
tolerance++;
}
break;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be simpler to just include the function name in the table, right beside all the other values, rather than needing to fill in a switch statement (because then there are two separate places to look for values and update, a table and a code switch)? e.g.

function getGemmPrecisionTolerance(shapeA, options) {
  const shapeA = resources.inputA.shape;
  options = resources.options;
  ...
}

...

const PrecisionMetrics = {
    batchNormalization: {ULP: {float32: 6, float16: 6}},
...
    gemm: {ULP: {float32: getGemmPrecisionTolerance, float16: getGemmPrecisionTolerance}}
...
    maxPool2d: {ULP: {float32: 0, float16: 0}},
...
};

...

function getPrecisonTolerance(opName, metricType, tensorDataType, resources) {
   let tolerance;
    if (metricType === 'ULP') {
      tolerance = PrecisionMetrics[opName].ULP[tensorDataType];
    }
    else if (metricType === 'ATOL') {
      tolerance = PrecisionMetrics[opName].ATOL[operandType];
    }

    // If the tolerance is dynamic, then evaluate the function to get the value.
    if (x instanceof Function) {
      tolerance = tolerance(resources);
    }
    return tolerance;
}

(but I'm also fine with the current code)

const checkResults = (opName, operandType, namedOutputOperands, outputs, resources) => {
const metricType = PrecisionMetrics[opName] ? Object.keys(PrecisionMetrics[opName])[0] : 'ULP';
const description = `test ${opName} ${operandType}`;
const tolerance = getPrecisonTolerance(opName, metricType, operandType, resources);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible someday that WebNN will support operators which return multiple outputs, and each output could have its own tolerance. It's not an issue yet for WebNN v1, and so I'm not worried about it, but we should keep that in mind when writing the code. The only operators I can think of right now are MAX_POOL and TOP_K, but those both expect exact results, and so both outputs have the same tolerance anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks for your insightful reminder.

Copy link
Contributor Author

@BruceDai BruceDai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Honry @fdwr, I've updated the commit to address your comments, please take another look.


const TypedArrayDict = {
float32: Float32Array,
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added these code, thanks.

* @param {Boolean} [options.bTranspose]
* @returns {Number} A tolerance number
*/
function getGemmPrecisionTolerance(shapeA, options) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestion, moved these functions outside.

}
tolerance++;
}
break;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

const checkResults = (opName, operandType, namedOutputOperands, outputs, resources) => {
const metricType = PrecisionMetrics[opName] ? Object.keys(PrecisionMetrics[opName])[0] : 'ULP';
const description = `test ${opName} ${operandType}`;
const tolerance = getPrecisonTolerance(opName, metricType, operandType, resources);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks for your insightful reminder.

@BruceDai
Copy link
Contributor Author

@fdwr Sorry. Updated, thanks.

Copy link

@fdwr fdwr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice. Thanks.

Copy link
Contributor

@huningxin huningxin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good with some nits, thanks!


// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-concat

const concatTests = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

concatTests looks common except the url of resources json file. Could it be moved to utils.js with a parameter for json file url?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @huningxin, thanks for your reviewing and suggestions.
Since there're different implementations (L#16 to L#29) to set targetTests for WebNN operations, for example the implementation for gemmTests likes:

  for (const test of tests) {
    const gemmOptions = {};
    const options = test.options;
    if (options !== undefined) {
      if (options.c !== undefined) {
        gemmOptions.c = {data: inputsData[options.c.data]};
        if (options.c.shape !== undefined) {
          gemmOptions.c['shape'] = options.c.shape;
        }
      }
      if (options.alpha !== undefined) {
        gemmOptions.alpha = inputsData[options.alpha.data];
      }
      if (options.beta !== undefined) {
        gemmOptions.beta = inputsData[options.beta.data];
      }
      if (options.aTranspose !== undefined) {
        gemmOptions.aTranspose = options.aTranspose;
      }
      if (options.bTranspose !== undefined) {
        gemmOptions.bTranspose = options.bTranspose;
      }
    }
    targetTests.push(
      {
        name: test.name,
        operandType: test.type,
        inputA: {shape: test.inputs.a.shape, data: inputsData[test.inputs.a.data]},
        inputB: {shape: test.inputs.b.shape, data: inputsData[test.inputs.b.data]},
        expected: {shape: test.expected.shape, data: {outputOperand: expectedData[test.expected.data]}},
        options: gemmOptions
      }
    );
  }

and implementation for clampTests likes:

  for (const test of tests) {
    const expectedDataSource = test.expected.data;
    const inputDataSource = test.input.data;
    targetTests.push(
      {
        name: test.name,
        operandType: test.type,
        input: {shape: test.input.shape, data: inputsData[inputDataSource]},
        expected: {shape: test.expected.shape, data: {outputOperand: expectedData[expectedDataSource]}},
        options: test.options
      }
    );
  }

so I implemented each <operation-name>Tests function for each operation, thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huningxin I just submitted tests for clamp (#37067) / gemm (#37068) / matmul (#37069) / reshape (#37070) / relu (#37071) / slice (#37072) operations, PTAL, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function looks like mainly setting up the test object for targetTests. It seems to me that all information and configuration of each test object come from the JOSN file. I am wondering: could we just define all these info in the JSON file and load it into resources. I suppose we don't need different JS implementation to setup test object for different ops.

Did I miss anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huningxin Do you suggest add actual test data for "data"? For example

"data": "float64Data"

my purpose of using the index string key of input / expected data is to reuse test data, thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huningxin I've updated this PR for more reuse code, please take another look. And there's a reusable tests demo on my own repo for your quickly review, thanks.

for (let i = 0; i < inputShapeValues.length; i++) {
inputOperands.push(builder.input('input' + i, {type: operandType, dimensions: inputShapeValues[i].shape}));
inputs['input' + i] = new TestTypedArray(inputShapeValues[i].data);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code that sets inputOperands and inputs look common for other operator tests. Could they be extract to utils.js?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huningxin Thanks for your suggestions, I will try to optimize setting inputOperands, inputs and outputs in the next PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building inputs from JSON looks good.

inputs['input' + i] = new TestTypedArray(inputShapeValues[i].data);
}
const outputOperand = builder.concat(inputOperands, resources.axis);
const outputs = {'outputOperand': new TestTypedArray(sizeOfShape(resources.expected.shape))};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto. The code that sets outputs looks common for other operator tests.

return [{outputOperand}, inputs, outputs];
};

testWebNNOperation('concat', concatTests(), buildGraph); No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be something like

testWebNNOperation('concat', '/webnn/resources/test_data/concat.json', (inputOperands, resources) => {
    return {outputOperand: builder.concat(inputOperands, resources.axis)}; });

Copy link
Contributor Author

@BruceDai BruceDai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huningxin Thanks for your reviewing and suggestions, I will follow up in next PR, thanks.


// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-concat

const concatTests = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @huningxin, thanks for your reviewing and suggestions.
Since there're different implementations (L#16 to L#29) to set targetTests for WebNN operations, for example the implementation for gemmTests likes:

  for (const test of tests) {
    const gemmOptions = {};
    const options = test.options;
    if (options !== undefined) {
      if (options.c !== undefined) {
        gemmOptions.c = {data: inputsData[options.c.data]};
        if (options.c.shape !== undefined) {
          gemmOptions.c['shape'] = options.c.shape;
        }
      }
      if (options.alpha !== undefined) {
        gemmOptions.alpha = inputsData[options.alpha.data];
      }
      if (options.beta !== undefined) {
        gemmOptions.beta = inputsData[options.beta.data];
      }
      if (options.aTranspose !== undefined) {
        gemmOptions.aTranspose = options.aTranspose;
      }
      if (options.bTranspose !== undefined) {
        gemmOptions.bTranspose = options.bTranspose;
      }
    }
    targetTests.push(
      {
        name: test.name,
        operandType: test.type,
        inputA: {shape: test.inputs.a.shape, data: inputsData[test.inputs.a.data]},
        inputB: {shape: test.inputs.b.shape, data: inputsData[test.inputs.b.data]},
        expected: {shape: test.expected.shape, data: {outputOperand: expectedData[test.expected.data]}},
        options: gemmOptions
      }
    );
  }

and implementation for clampTests likes:

  for (const test of tests) {
    const expectedDataSource = test.expected.data;
    const inputDataSource = test.input.data;
    targetTests.push(
      {
        name: test.name,
        operandType: test.type,
        input: {shape: test.input.shape, data: inputsData[inputDataSource]},
        expected: {shape: test.expected.shape, data: {outputOperand: expectedData[expectedDataSource]}},
        options: test.options
      }
    );
  }

so I implemented each <operation-name>Tests function for each operation, thanks.

let position = 0;
for (const shape of inputShapes) {
const size = sizeOfShape(shape);
inputShapeValues.push({shape, data: inputsData[inputDataSource].slice(position, position + size)});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is especial to concat op. To inputs of others operations, it's simply to set data. @huningxin

for (let i = 0; i < inputShapeValues.length; i++) {
inputOperands.push(builder.input('input' + i, {type: operandType, dimensions: inputShapeValues[i].shape}));
inputs['input' + i] = new TestTypedArray(inputShapeValues[i].data);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huningxin Thanks for your suggestions, I will try to optimize setting inputOperands, inputs and outputs in the next PR.

Copy link
Contributor Author

@BruceDai BruceDai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huningxin Thanks for your reviewing and suggestions.

const inputShapeValues = resources.inputShapeValues;
const operandType = resources.operandType;
const TestTypedArray = TypedArrayDict[operandType];
const computeConcat = (builder, resources) => {
Copy link
Contributor Author

@BruceDai BruceDai Nov 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I will update it to buildConcat.

aTranspose: false,
bTranspose: false
}
const options = resources.options ? resources.options : defaultOptions;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any method to get this 'MLGemmOptions' from browser? Thanks.

* @param {String[]} nameArray
* @returns {MLOperand[]} Input operands
*/
const createInputOperands = (builder, resources, nameArray) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the inputs of concat is a sequence, it's special to other ops.
I form its inputs, like

      "inputs": [
        {
          "name": "input1",
          "shape": [12],
          "data": [
            -0.39444134019222243,
            ...
          ]
        },
        {
          "name": "input2",
          "shape": [12],
          "data": [
            0.4918989118791477,
            ....
          ]
        }
      ],

while for inputs of clamp, I form it like

      "inputs": {
        "x": { // use "x" for input operand name
            "shape": [12],
            "data": [
              -0.39444134019222243,
              ...
          -  ]
          }
        }

so this createInputOperands is for other ops, please see the usage on demo tests .

* Build a graph.
* @param {MLGraphBuilder} builder - A ML graph builder
* @param {Object} resources - Resources used for building a graph
* @param {Function} computeFunc - A compute function
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will update it.

0.1211843192577362
]
},
"type": "float32"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will update it. And is it ok to still keep "type" here to indicate that current test is for float32 precision?

@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch from 36c1f89 to c9737fc Compare November 28, 2022 03:50
@BruceDai
Copy link
Contributor Author

@huningxin I've updated PR to address your comments, please take another look, thanks.

@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch from c9737fc to dabd6b0 Compare November 28, 2022 05:09
@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch from dabd6b0 to 75094a4 Compare November 28, 2022 07:52
@BruceDai
Copy link
Contributor Author

@huningxin I updated to fix your last comments, PTAL, thanks.

@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch from 75094a4 to e0ab7ca Compare November 28, 2022 08:35
@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch from e0ab7ca to d17dab8 Compare November 29, 2022 05:21
@BruceDai
Copy link
Contributor Author

@huningxin Updated, please take another look, thanks.

Copy link
Contributor

@huningxin huningxin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks @BruceDai !

@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch 2 times, most recently from 35f9b64 to 7767126 Compare November 30, 2022 04:12
@BruceDai BruceDai mentioned this pull request Nov 30, 2022
@BruceDai BruceDai force-pushed the add_webnn_concat_tests branch from 7767126 to de07f07 Compare January 6, 2023 05:20
@BruceDai
Copy link
Contributor Author

BruceDai commented Jan 6, 2023

@dontcallmedom @Honry, I rebased with latest code, all checks have passed, PTAL, thanks.

@Honry Honry merged commit ffcdfb5 into web-platform-tests:master Jan 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants