-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Broadcasting functions #3624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Broadcasting functions #3624
Changes from all commits
6b0ea89
cd83def
f2a2943
756aa83
a804f9d
cfe6490
5224a45
b212e6f
4315651
c99c48a
d749405
c267a8c
23bc583
26367b5
54338f5
5e13b30
d593869
41d2db1
bb50f2b
826d36e
9f761fc
1b4afd9
215502f
3a19f48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| export const broadcastMatricesDocs = { | ||
| name: 'broadcastMatrices', | ||
| category: 'Matrix', | ||
| syntax: [ | ||
| 'broadcastMatrices(A, B)', | ||
| 'broadcastMatrices(A, B, ...)' | ||
| ], | ||
| description: 'Broadcast any number of arrays or matrices against each other.', | ||
| examples: [ | ||
| 'broadcastMatrices([1, 2, 3], [[1], [2], [3]])', | ||
| 'broadcastMatrices([1, 2; 3, 4], [5, 6; 7, 8])', | ||
| 'broadcastMatrices([1, 2, 3], [5], [[10], [20], [30]])' | ||
| ], | ||
| seealso: [ | ||
| 'size', 'reshape', 'broadcastSizes', 'broadcastTo' | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| export const broadcastSizesDocs = { | ||
| name: 'broadcastSizes', | ||
| category: 'Matrix', | ||
| syntax: [ | ||
| 'broadcastSizes(sizeA, sizeB)', | ||
| 'broadcastSizes(sizeA, sizeB, ...)' | ||
| ], | ||
| description: 'Broadcast the sizes of matrices to a compatible size. Computes the size of the resulting matrix when broadcasting the input sizes against each other.', | ||
| examples: [ | ||
| 'broadcastSizes([3, 1, 3], [3, 3])', | ||
| 'broadcastSizes([2, 1], [2, 2])', | ||
| 'broadcastSizes([1, 3], [2, 3], [4, 2, 3])' | ||
| ], | ||
| seealso: [ | ||
| 'size', 'reshape', 'broadcastTo', 'broadcastMatrices' | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| export const broadcastToDocs = { | ||
| name: 'broadcastTo', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I worry about the name of this function. It seems to me that since sizes look like matrices, visually,
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand. Many of these are taken from numpy and have counterparts in jax / mlx / pytorch and maybe others.
I don't have a strong opinion on this, just please review if it makes sense to follow that convention.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am the one less familiar with the territory here. That's why this was couched as a suggestion. Please select the name you think is best, including leaving it be, unless @josdejong weighs in otherwise. Please just post your final decision here.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, to land this PR we need a decision on the final name here. If you are on the fence, I recommend switching to
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree about the differences between shape and size. I'm considering |
||
| category: 'Matrix', | ||
| syntax: [ | ||
| 'broadcastTo(A, size)' | ||
| ], | ||
| description: 'Broadcast a matrix to a compatible size', | ||
| examples: [ | ||
| 'broadcastTo([1, 2, 3], [3, 3])', | ||
| 'broadcastTo([1, 2; 3, 4], [2, 2])' | ||
| ], | ||
| seealso: [ | ||
| 'size', 'reshape', 'broadcastSizes', 'broadcastMatrices' | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import { broadcastArrays } from '../../utils/array.js' | ||
| import { factory } from '../../utils/factory.js' | ||
| import { isMatrix } from '../../utils/is.js' | ||
|
|
||
| const name = 'broadcastMatrices' | ||
| const dependencies = ['typed'] | ||
|
|
||
| export const createBroadcastMatrices = /* #__PURE__ */ factory(name, dependencies, ({ typed }) => { | ||
| /** | ||
| * Broadcast any number of arrays or matrices against each other. | ||
| * The broadcasting rules can be found in [Matrices#Broadcasting](./datatype/matrices#Broadcasting). | ||
| * | ||
| * Syntax: | ||
| * | ||
| * math.broadcastMatrices(x, y) | ||
| * math.broadcastMatrices(x, y, ...) | ||
| * | ||
| * Examples: | ||
| * | ||
| * math.broadcastMatrices([1, 2], [[3], [4]]) // returns [[[1, 2], [1, 2]], [[3, 3], [4, 4]]] | ||
| * math.broadcastMatrices([2, 3]) // returns [[2, 3]] | ||
| * math.broadcastMatrices([2, 3], [3, 1]) // returns [[2, 3], [3, 1]] | ||
| * | ||
| * See also: | ||
| * | ||
| * size, reshape, broadcastSizes, broadcastTo | ||
| * | ||
| * History: | ||
| * | ||
| * v15.1.1 created | ||
| * @param {...(Array|Matrix)} x One or more matrices or arrays | ||
| * @return {Array[Array|Matrix]} An array of matrices with the broadcasted sizes. | ||
| */ | ||
| return typed(name, { | ||
| '...Array|Matrix': collections => { | ||
| const areMatrices = collections.map(isMatrix) | ||
| if (areMatrices.includes(true)) { | ||
| const arrays = collections.map((c, i) => areMatrices[i] ? c.valueOf() : c) | ||
| const broadcastedArrays = broadcastArrays(...arrays) | ||
| const broadcastedCollections = broadcastedArrays.map((arr, i) => areMatrices[i] ? collections[i].create(arr) : arr) | ||
| return broadcastedCollections | ||
| } | ||
| return broadcastArrays(...collections) | ||
| } | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import { broadcastSizes } from '../../utils/array.js' | ||
| import { factory } from '../../utils/factory.js' | ||
|
|
||
| const name = 'broadcastSizes' | ||
| const dependencies = ['typed'] | ||
|
|
||
| export const createBroadcastSizes = /* #__PURE__ */ factory(name, dependencies, ({ typed }) => { | ||
| /** | ||
| * Calculate the broadcasted size of one or more matrices or arrays. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As per my comments on the internal docs, shouldn't this be something more like "Calculate the size that would result from broadcasting one or more matrices or arrays, given the sizes of the input collections."? The same comments about having documentation on the operation of broadcasting either here or linked here apply to this function as well. Also mention of what happens with incompatible sizes.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still needs further editing/documentation. |
||
| * Always returns an Array containing numbers. | ||
| * The broadcasting rules can be found in [Matrices#Broadcasting](./datatype/matrices#Broadcasting).* | ||
| * Syntax: | ||
| * | ||
| * math.broadcastSizes(x, y) | ||
| * math.broadcastSizes(x, y, ...) | ||
| * | ||
| * Examples: | ||
| * | ||
| * math.broadcastSizes([2, 3]) // returns [2, 3] | ||
| * math.broadcastSizes([2, 3], [3]) // returns [2, 3] | ||
| * math.broadcastSizes([1, 2, 3], [1, 2, 1]) // returns [1, 2, 3] | ||
| * | ||
| * See also: | ||
| * | ||
| * size, reshape, squeeze, broadcastTo | ||
| * | ||
| * History: | ||
| * | ||
| * v15.1.1 created | ||
| * @param {...(Array|Matrix)} x One or more matrices or arrays | ||
| * @return {Array} A vector with the broadcasted size. | ||
| */ | ||
| return typed(name, { | ||
| '...Array|Matrix': collections => broadcastSizes(...collections.map(collection => collection.valueOf())) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { broadcastTo } from '../../utils/array.js' | ||
| import { factory } from '../../utils/factory.js' | ||
|
|
||
| const name = 'broadcastTo' | ||
| const dependencies = ['typed'] | ||
|
|
||
| export const createBroadcastTo = /* #__PURE__ */ factory(name, dependencies, ({ typed }) => { | ||
| /** | ||
| * Broadcast an array to a specified size. | ||
| * The broadcasting rules can be found in [Matrices#Broadcasting](./datatype/matrices#Broadcasting). | ||
| * | ||
| * Syntax: | ||
| * | ||
| * math.broadcastTo(x, size) | ||
| * | ||
| * Examples: | ||
| * | ||
| * math.broadcastTo([1, 2, 3], [2, 3]) // returns [[1, 2, 3], [1, 2, 3]] | ||
| * math.broadcastTo([2, 3], [2, 2]) // returns [[2, 3], [2, 3]] | ||
| * | ||
| * See also: | ||
| * | ||
| * size, reshape, squeeze, broadcastSizes | ||
| * | ||
| * History: | ||
| * | ||
| * v15.1.1 created | ||
| * | ||
| * @param {Array|Matrix} x The array or matrix to broadcast | ||
| * @param {Array|Matrix} size The target size | ||
| * @return {Array} The broadcasted array | ||
| */ | ||
| return typed(name, { | ||
| 'Array, Array': broadcastTo, | ||
| 'Array, Matrix': (arr, size) => broadcastTo(arr, size.valueOf()), | ||
| 'Matrix, Array|Matrix': function (M, size) { | ||
| return M.create({ | ||
| data: broadcastTo(M.valueOf(), size.valueOf()), | ||
| size: size.valueOf() | ||
| }, | ||
| M.datatype()) | ||
| } | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import assert from 'assert' | ||
| import math from '../../../../src/defaultInstance.js' | ||
|
|
||
| describe('broadcastMatrices', function () { | ||
| const matrix = math.matrix | ||
| const broadcastMatrices = math.broadcastMatrices | ||
| const A = [[1], [2], [3]] | ||
| const B = [[10, 20, 30]] | ||
| const C = [100] | ||
| const broadcastedA = [[1, 1, 1], [2, 2, 2], [3, 3, 3]] | ||
| const broadcastedB = [[10, 20, 30], [10, 20, 30], [10, 20, 30]] | ||
| const broadcastedC = [[100, 100, 100], [100, 100, 100], [100, 100, 100]] | ||
|
|
||
| it('should broadcast matrices', function () { | ||
| assert.deepStrictEqual(broadcastMatrices(matrix(A), matrix(B)), [matrix(broadcastedA), matrix(broadcastedB)]) | ||
| assert.deepStrictEqual(broadcastMatrices(matrix(A), matrix(C)), [matrix(A), matrix([[100], [100], [100]])]) | ||
| assert.deepStrictEqual(broadcastMatrices(matrix(B), matrix(A)), [matrix(broadcastedB), matrix(broadcastedA)]) | ||
| assert.deepStrictEqual(broadcastMatrices(matrix(A), matrix(B), matrix(C)), [matrix(broadcastedA), matrix(broadcastedB), matrix(broadcastedC)]) | ||
| }) | ||
|
|
||
| it('should broadcast arrays', function () { | ||
| assert.deepStrictEqual(broadcastMatrices(A, B), [broadcastedA, broadcastedB]) | ||
| assert.deepStrictEqual(broadcastMatrices(B, A), [broadcastedB, broadcastedA]) | ||
| assert.deepStrictEqual(broadcastMatrices(A, B, C), [broadcastedA, broadcastedB, broadcastedC]) | ||
| assert.deepStrictEqual(broadcastMatrices(A), [A]) | ||
| assert.deepStrictEqual(broadcastMatrices(B, C, A), [broadcastedB, broadcastedC, broadcastedA]) | ||
| }) | ||
|
|
||
| it('should throw an error if sizes are not compatible', function () { | ||
| assert.throws(function () { broadcastMatrices(matrix([[1, 2], [3, 4]]), matrix([[1, 2, 3]])) }, /Error: shape mismatch: /) | ||
| assert.throws(function () { broadcastMatrices([[1, 2], [3, 4]], matrix([[1, 2, 3]])) }, /Error: shape mismatch: /) | ||
| assert.throws(function () { broadcastMatrices(matrix([[1, 2], [3, 4]]), [[1, 2, 3]]) }, /Error: shape mismatch: /) | ||
| assert.throws(function () { broadcastMatrices([[1, 2], [3, 4]], [[1, 2, 3]]) }, /Error: shape mismatch: /) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import assert from 'assert' | ||
| import math from '../../../../src/defaultInstance.js' | ||
|
|
||
| describe('broadcastSizes', function () { | ||
| const broadcastSizes = math.broadcastSizes | ||
| const matrix = math.matrix | ||
|
|
||
| it('should broadcast sizes', function () { | ||
| assert.deepStrictEqual(broadcastSizes([2, 3]), [2, 3]) | ||
| assert.deepStrictEqual(broadcastSizes([3, 3], [3, 1]), [3, 3]) | ||
| assert.deepStrictEqual(broadcastSizes([2, 1], [1, 3]), [2, 3]) | ||
| assert.deepStrictEqual(broadcastSizes([5, 4, 3], [1, 4, 1]), [5, 4, 3]) | ||
| assert.deepStrictEqual(broadcastSizes([3], [2, 3]), [2, 3]) | ||
| assert.deepStrictEqual(broadcastSizes([1, 3], [2, 1]), [2, 3]) | ||
| }) | ||
|
|
||
| it('should throw an error if sizes are not compatible', function () { | ||
| assert.throws(function () { broadcastSizes([2, 3], [3, 2]) }, /Error: shape mismatch: /) | ||
| assert.throws(function () { broadcastSizes([2, 3], [2, 3, 4]) }, /Error: shape mismatch: /) | ||
| }) | ||
|
|
||
| it('should broadcast sizes of mixed arrays and matrices', function () { | ||
| assert.deepStrictEqual(broadcastSizes([3, 3], matrix([3, 1])), [3, 3]) | ||
| assert.deepStrictEqual(broadcastSizes(matrix([2, 1]), [1, 3]), [2, 3]) | ||
| assert.deepStrictEqual(broadcastSizes(matrix([5, 4, 3]), matrix([1, 4, 1])), [5, 4, 3]) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import assert from 'assert' | ||
| import math from '../../../../src/defaultInstance.js' | ||
|
|
||
| describe('broadcastTo', function () { | ||
| const broadcastTo = math.broadcastTo | ||
| const matrix = math.matrix | ||
|
|
||
| it('should broadcast arrays to a given size', function () { | ||
| assert.deepStrictEqual(broadcastTo([1, 2, 3], [2, 3]), [[1, 2, 3], [1, 2, 3]]) | ||
| assert.deepStrictEqual(broadcastTo([2, 3], [2, 2]), [[2, 3], [2, 3]]) | ||
| }) | ||
|
|
||
| it('should broadcast matrices to a given size', function () { | ||
| assert.deepStrictEqual(broadcastTo(matrix([1, 2, 3]), [2, 3]), matrix([[1, 2, 3], [1, 2, 3]])) | ||
| assert.deepStrictEqual(broadcastTo(matrix([2, 3]), [2, 2]), matrix([[2, 3], [2, 3]])) | ||
| }) | ||
|
|
||
| it('should broadcast mixed arrays and matrices to a given size', function () { | ||
| assert.deepStrictEqual(broadcastTo([1, 2, 3], matrix([2, 3])), [[1, 2, 3], [1, 2, 3]]) | ||
| assert.deepStrictEqual(broadcastTo(matrix([2, 3]), [2, 2]), matrix([[2, 3], [2, 3]])) | ||
| assert.deepStrictEqual(broadcastTo(matrix([1, 2, 3]), matrix([2, 3])), matrix([[1, 2, 3], [1, 2, 3]])) | ||
| assert.deepStrictEqual(broadcastTo([2, 3], matrix([2, 2])), [[2, 3], [2, 3]]) | ||
| }) | ||
|
|
||
| it('should throw an error if sizes are not compatible', function () { | ||
| assert.throws(function () { broadcastTo([1, 2], [2, 3]) }, /Error: shape mismatch: /) | ||
| assert.throws(function () { broadcastTo(matrix([1, 2]), [2, 3]) }, /Error: shape mismatch: /) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the syntax read
'broadcastMatrices(A, B, ...)'since any number of arguments are allowed?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, yes you are right. Fixed