(
op: CumOpType, x: TensorInfo, backend: WebGPUBackend, axis: number,
exclusive: boolean, reverse: boolean)
| 24 | import {transpose} from './Transpose'; |
| 25 | |
| 26 | export function cumImpl( |
| 27 | op: CumOpType, x: TensorInfo, backend: WebGPUBackend, axis: number, |
| 28 | exclusive: boolean, reverse: boolean): TensorInfo { |
| 29 | const xRank = x.shape.length; |
| 30 | const permutation = backend_util.getAxesPermutation([axis], xRank); |
| 31 | let permutedX = x; |
| 32 | if (permutation != null) { |
| 33 | permutedX = transpose({inputs: {x}, backend, attrs: {perm: permutation}}); |
| 34 | } |
| 35 | const permutedAxis = backend_util.getInnerMostAxes(1, xRank)[0]; |
| 36 | |
| 37 | if (permutedAxis !== xRank - 1) { |
| 38 | throw new Error( |
| 39 | `WebGPU cumprod shader expects an inner-most axis=${ |
| 40 | x.shape.length - 1} ` + |
| 41 | `but got axis=${axis}`); |
| 42 | } |
| 43 | const size = permutedX.shape[permutedAxis]; |
| 44 | let result = identity({inputs: {x: permutedX}, backend}); |
| 45 | // Use cum parallel algorithm, inspired by: |
| 46 | // https://developer.nvidia.com/gpugems/gpugems3/part-vi-gpu-computing/chapter-39-parallel-prefix-sum-scan-cuda |
| 47 | // Note: although the algorithm is called sum, it works for any associtative |
| 48 | // operator with an identity. |
| 49 | |
| 50 | for (let i = 0; i <= Math.ceil(Math.log2(size)) - 1; i++) { |
| 51 | const program = new CumProgram(op, permutedX.shape, false, reverse); |
| 52 | const prevResult = result; |
| 53 | const uniformData = [{type: 'float32', data: [i]}]; |
| 54 | result = |
| 55 | backend.runWebGPUProgram(program, [result], result.dtype, uniformData); |
| 56 | backend.disposeData(prevResult.dataId); |
| 57 | } |
| 58 | // For exclusive cum, shift the end result in the direction of product or sum |
| 59 | // and add 1 for product or 0 for sum to the front index. |
| 60 | if (exclusive) { |
| 61 | const program = new CumProgram(op, permutedX.shape, exclusive, reverse); |
| 62 | const prevResult = result; |
| 63 | const uniformData = [{type: 'float32', data: [0]}]; |
| 64 | result = |
| 65 | backend.runWebGPUProgram(program, [result], result.dtype, uniformData); |
| 66 | backend.disposeData(prevResult.dataId); |
| 67 | } |
| 68 | |
| 69 | if (permutation != null) { |
| 70 | const reversePermutation = backend_util.getUndoAxesPermutation(permutation); |
| 71 | const reverseTransposedResult = transpose( |
| 72 | {inputs: {x: result}, backend, attrs: {perm: reversePermutation}}); |
| 73 | |
| 74 | backend.disposeData(result.dataId); |
| 75 | backend.disposeData(permutedX.dataId); |
| 76 | |
| 77 | return reverseTransposedResult; |
| 78 | } |
| 79 | |
| 80 | return result; |
| 81 | } |
no test coverage detected
searching dependent graphs…