diff --git a/.github/.keepalive b/.github/.keepalive
deleted file mode 100644
index 6fc954e..0000000
--- a/.github/.keepalive
+++ /dev/null
@@ -1 +0,0 @@
-2024-08-03T20:18:36.587Z
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ccbbf78..4282ed0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,50 @@
> Package changelog.
+
+
+## Unreleased (2024-08-17)
+
+
+
+### Features
+
+- [`0eaf8b6`](https://github.com/stdlib-js/stdlib/commit/0eaf8b6263017bfe14c1b8769dfd885a19d1778e) - add support for operating on stacks of vectors
+
+
+
+
+
+
+
+### Commits
+
+
+
+- [`0eaf8b6`](https://github.com/stdlib-js/stdlib/commit/0eaf8b6263017bfe14c1b8769dfd885a19d1778e) - **feat:** add support for operating on stacks of vectors _(by Athan Reines)_
+
+
+
+
+
+
+
+
+
+### Contributors
+
+A total of 1 person contributed to this release. Thank you to this contributor:
+
+- Athan Reines
+
+
+
+
+
+
+
+
+
## 0.2.2 (2024-07-28)
@@ -84,8 +128,7 @@ A total of 1 person contributed to this release. Thank you to this contributor:
### BREAKING CHANGES
-- [`cca37d0`](https://github.com/stdlib-js/stdlib/commit/cca37d051d8c0209970fc681353fdb4e4d257a8a): update minimum TypeScript version
-- [`cca37d0`](https://github.com/stdlib-js/stdlib/commit/cca37d051d8c0209970fc681353fdb4e4d257a8a): update minimum TypeScript version to 4.1
+- [`cca37d0`](https://github.com/stdlib-js/stdlib/commit/cca37d051d8c0209970fc681353fdb4e4d257a8a): update minimum TypeScript version to 4.1
- To migrate, users should upgrade their TypeScript version to at least version 4.1.
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 26a1c46..57d1184 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -46,6 +46,7 @@ Marcus Fantham
Matt Cochrane
Mihir Pandit <129577900+MSP20086@users.noreply.github.com>
Milan Raj
+Mohammad Kaif <98884589+Kaif987@users.noreply.github.com>
Momtchil Momtchev
Muhammad Haris
Naresh Jagadeesan
@@ -70,6 +71,7 @@ Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com>
Rutam <138517416+performant23@users.noreply.github.com>
Ryan Seal
Sai Srikar Dumpeti <80447788+the-r3aper7@users.noreply.github.com>
+SarthakPaandey <145528240+SarthakPaandey@users.noreply.github.com>
Seyyed Parsa Neshaei
Shashank Shekhar Singh
Shivam <11shivam00@gmail.com>
diff --git a/README.md b/README.md
index 60b0b3b..a939f90 100644
--- a/README.md
+++ b/README.md
@@ -84,7 +84,7 @@ To view installation and usage instructions specific to each branch build, be su
var ddot = require( '@stdlib/blas-ddot' );
```
-#### ddot( x, y )
+#### ddot( x, y\[, dim] )
Calculates the dot product of two double-precision floating-point vectors `x` and `y`.
@@ -96,25 +96,38 @@ var x = array( new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0 ] ) );
var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );
var z = ddot( x, y );
+// returns
+
+var v = z.get();
// returns -5.0
```
The function has the following parameters:
-- **x**: a 1-dimensional [`ndarray`][@stdlib/ndarray/array] whose underlying data type is `float64`.
-- **y**: a 1-dimensional [`ndarray`][@stdlib/ndarray/array] whose underlying data type is `float64`.
+- **x**: a non-zero-dimensional [`ndarray`][@stdlib/ndarray/ctor] whose underlying data type is `float64`. Must be [broadcast-compatible][@stdlib/ndarray/base/broadcast-shapes] with `y`.
+- **y**: a non-zero-dimensional [`ndarray`][@stdlib/ndarray/ctor] whose underlying data type is `float64`. Must be [broadcast-compatible][@stdlib/ndarray/base/broadcast-shapes] with `x`.
+- **dim**: dimension for which to compute the dot product. Must be a negative integer. Negative indices are resolved relative to the last array dimension, with the last dimension corresponding to `-1`. Default: `-1`.
-If provided empty vectors, the function returns `0.0`.
+If provided at least one input [`ndarray`][@stdlib/ndarray/ctor] having more than one dimension, the input [`ndarrays`][@stdlib/ndarray/ctor] are [broadcasted][@stdlib/ndarray/base/broadcast-shapes] to a common shape. For multi-dimensional input [`ndarrays`][@stdlib/ndarray/ctor], the function performs batched computation, such that the function computes the dot product for each pair of vectors in `x` and `y` according to the specified dimension index.
```javascript
var Float64Array = require( '@stdlib/array-float64' );
var array = require( '@stdlib/ndarray-array' );
-var x = array( new Float64Array() );
-var y = array( new Float64Array() );
+var opts = {
+ 'shape': [ 2, 3 ]
+};
+var x = array( new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0, 3.0 ] ), opts );
+var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0, 2.0 ] ), opts );
var z = ddot( x, y );
-// returns 0.0
+// returns
+
+var v1 = z.get( 0 );
+// returns 23.0
+
+var v2 = z.get( 1 );
+// returns -22.0
```
@@ -125,6 +138,11 @@ var z = ddot( x, y );
## Notes
+- The size of the contracted dimension must be the same for both input [`ndarrays`][@stdlib/ndarray/ctor].
+- The function resolves the dimension index for which to compute the dot product **before** broadcasting.
+- Negative indices are resolved relative to the last [`ndarray`][@stdlib/ndarray/ctor] dimension, with the last dimension corresponding to `-1`.
+- The output [`ndarray`][@stdlib/ndarray/ctor] has the same data type as the input [`ndarrays`][@stdlib/ndarray/ctor] and has a shape which is determined by broadcasting and excludes the contracted dimension.
+- If provided empty vectors, the dot product is `0`.
- `ddot()` provides a higher-level interface to the [BLAS][blas] level 1 function [`ddot`][@stdlib/blas/base/ddot].
@@ -138,27 +156,27 @@ var z = ddot( x, y );
```javascript
-var discreteUniform = require( '@stdlib/random-base-discrete-uniform' );
-var Float64Array = require( '@stdlib/array-float64' );
+var discreteUniform = require( '@stdlib/random-array-discrete-uniform' );
+var ndarray2array = require( '@stdlib/ndarray-to-array' );
var array = require( '@stdlib/ndarray-array' );
var ddot = require( '@stdlib/blas-ddot' );
-var x = array( new Float64Array( 10 ) );
-var y = array( new Float64Array( 10 ) );
+var opts = {
+ 'dtype': 'float64'
+};
-var rand1 = discreteUniform.factory( 0, 100 );
-var rand2 = discreteUniform.factory( 0, 10 );
+var x = array( discreteUniform( 10, 0, 100, opts ), {
+ 'shape': [ 5, 2 ]
+});
+console.log( ndarray2array( x ) );
-var i;
-for ( i = 0; i < x.length; i++ ) {
- x.set( i, rand1() );
- y.set( i, rand2() );
-}
-console.log( x.toString() );
-console.log( y.toString() );
+var y = array( discreteUniform( 10, 0, 10, opts ), {
+ 'shape': x.shape
+});
+console.log( ndarray2array( y ) );
-var z = ddot( x, y );
-console.log( z );
+var z = ddot( x, y, -1 );
+console.log( ndarray2array( z ) );
```
@@ -257,7 +275,9 @@ Copyright © 2016-2024. The Stdlib [Authors][stdlib-authors].
[blas]: http://www.netlib.org/blas
-[@stdlib/ndarray/array]: https://github.com/stdlib-js/ndarray-array
+[@stdlib/ndarray/ctor]: https://github.com/stdlib-js/ndarray-ctor
+
+[@stdlib/ndarray/base/broadcast-shapes]: https://github.com/stdlib-js/ndarray-base-broadcast-shapes
diff --git a/benchmark/benchmark.js b/benchmark/benchmark.js
index a14b80d..204e147 100644
--- a/benchmark/benchmark.js
+++ b/benchmark/benchmark.js
@@ -21,15 +21,21 @@
// MODULES //
var bench = require( '@stdlib/bench-harness' );
-var randu = require( '@stdlib/random-base-randu' );
var isnan = require( '@stdlib/math-base-assert-is-nan' );
var pow = require( '@stdlib/math-base-special-pow' );
-var Float64Array = require( '@stdlib/array-float64' );
+var uniform = require( '@stdlib/random-array-uniform' );
var array = require( '@stdlib/ndarray-array' );
var pkg = require( './../package.json' ).name;
var ddot = require( './../lib/main.js' );
+// VARIABLES //
+
+var opts = {
+ 'dtype': 'float64'
+};
+
+
// FUNCTIONS //
/**
@@ -40,21 +46,16 @@ var ddot = require( './../lib/main.js' );
* @returns {Function} benchmark function
*/
function createBenchmark( len ) {
- var x;
- var y;
- var i;
-
- x = new Float64Array( len );
- y = new Float64Array( len );
- for ( i = 0; i < len; i++ ) {
- x[ i ] = ( randu()*10.0 ) - 20.0;
- y[ i ] = ( randu()*10.0 ) - 20.0;
- }
- x = array( x );
- y = array( y );
-
+ var x = array( uniform( len, -100.0, 100.0, opts ) );
+ var y = array( uniform( len, -100.0, 100.0, opts ) );
return benchmark;
+ /**
+ * Benchmark function.
+ *
+ * @private
+ * @param {Benchmark} b - benchmark instance
+ */
function benchmark( b ) {
var d;
var i;
@@ -62,12 +63,12 @@ function createBenchmark( len ) {
b.tic();
for ( i = 0; i < b.iterations; i++ ) {
d = ddot( x, y );
- if ( isnan( d ) ) {
+ if ( isnan( d.get() ) ) {
b.fail( 'should not return NaN' );
}
}
b.toc();
- if ( isnan( d ) ) {
+ if ( isnan( d.get() ) ) {
b.fail( 'should not return NaN' );
}
b.pass( 'benchmark finished' );
@@ -96,7 +97,7 @@ function main() {
for ( i = min; i <= max; i++ ) {
len = pow( 10, i );
f = createBenchmark( len );
- bench( pkg+':len='+len, f );
+ bench( pkg+'::vectors:len='+len, f );
}
}
diff --git a/benchmark/benchmark.stacks.js b/benchmark/benchmark.stacks.js
new file mode 100644
index 0000000..b5bdcbd
--- /dev/null
+++ b/benchmark/benchmark.stacks.js
@@ -0,0 +1,122 @@
+/**
+* @license Apache-2.0
+*
+* Copyright (c) 2020 The Stdlib Authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+'use strict';
+
+// MODULES //
+
+var bench = require( '@stdlib/bench-harness' );
+var isnan = require( '@stdlib/math-base-assert-is-nan' );
+var pow = require( '@stdlib/math-base-special-pow' );
+var uniform = require( '@stdlib/random-array-uniform' );
+var numel = require( '@stdlib/ndarray-base-numel' );
+var array = require( '@stdlib/ndarray-array' );
+var pkg = require( './../package.json' ).name;
+var ddot = require( './../lib/main.js' );
+
+
+// VARIABLES //
+
+var OPTS = {
+ 'dtype': 'float64'
+};
+
+
+// FUNCTIONS //
+
+/**
+* Creates a benchmark function.
+*
+* @private
+* @param {PositiveIntegerArray} shape - array shape
+* @returns {Function} benchmark function
+*/
+function createBenchmark( shape ) {
+ var x;
+ var y;
+ var N;
+ var o;
+
+ N = numel( shape );
+ o = {
+ 'shape': shape
+ };
+ x = array( uniform( N, -100.0, 100.0, OPTS ), o );
+ y = array( uniform( N, -100.0, 100.0, OPTS ), o );
+
+ return benchmark;
+
+ /**
+ * Benchmark function.
+ *
+ * @private
+ * @param {Benchmark} b - benchmark instance
+ */
+ function benchmark( b ) {
+ var d;
+ var i;
+
+ b.tic();
+ for ( i = 0; i < b.iterations; i++ ) {
+ d = ddot( x, y );
+ if ( isnan( d.iget( 0 ) ) ) {
+ b.fail( 'should not return NaN' );
+ }
+ }
+ b.toc();
+ if ( isnan( d.iget( 0 ) ) ) {
+ b.fail( 'should not return NaN' );
+ }
+ b.pass( 'benchmark finished' );
+ b.end();
+ }
+}
+
+
+// MAIN //
+
+/**
+* Main execution sequence.
+*
+* @private
+*/
+function main() {
+ var shape;
+ var min;
+ var max;
+ var N;
+ var f;
+ var i;
+
+ min = 1; // 10^min
+ max = 6; // 10^max
+
+ for ( i = min; i <= max; i++ ) {
+ N = pow( 10, i );
+
+ shape = [ 2, N/2 ];
+ f = createBenchmark( shape );
+ bench( pkg+'::stacks:size='+N+',ndims='+shape.length+',shape=('+shape.join( ',' )+')', f );
+
+ shape = [ N/2, 2 ];
+ f = createBenchmark( shape );
+ bench( pkg+'::stacks:size='+N+',ndims='+shape.length+',shape=('+shape.join( ',' )+')', f );
+ }
+}
+
+main();
diff --git a/dist/index.js b/dist/index.js
index 45ab85a..dd8a7f8 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1,5 +1,5 @@
-"use strict";var o=function(r,e){return function(){return e||r((e={exports:{}}).exports,e),e.exports}};var i=o(function(g,t){
-var a=require('@stdlib/assert-is-float64vector-like/dist'),n=require('@stdlib/error-tools-fmtprodmsg/dist'),s=require('@stdlib/blas-base-ddot/dist').ndarray;function u(r,e){if(!a(r))throw new TypeError(n('0CHD8',r));if(!a(e))throw new TypeError(n('0CHD9',e));if(r.length!==e.length)throw new RangeError(n('0CH3S',r.length,e.length));return s(r.length,r.data,r.strides[0],r.offset,e.data,e.strides[0],e.offset)}t.exports=u
-});var d=i();module.exports=d;
+"use strict";var S=function(s,a){return function(){return a||s((a={exports:{}}).exports,a),a.exports}};var T=S(function(N,E){
+var w=require('@stdlib/assert-is-float64ndarray-like/dist'),V=require('@stdlib/assert-is-negative-integer/dist').isPrimitive,k=require('@stdlib/math-base-special-fast-min/dist'),F=require('@stdlib/array-base-without/dist'),I=require('@stdlib/ndarray-base-numel/dist'),j=require('@stdlib/ndarray-base-normalize-index/dist'),z=require('@stdlib/ndarray-base-maybe-broadcast-arrays/dist'),b=require('@stdlib/ndarray-base-ndarraylike2ndarray/dist'),q=require('@stdlib/ndarray-iter-stacks/dist'),D=require('@stdlib/ndarray-empty/dist'),x=require('@stdlib/blas-base-ddot/dist').ndarray,i=require('@stdlib/error-tools-fmtprodmsg/dist');function R(s,a){var r,o,n,v,y,c,u,f,e,t,m,l,p,d,g,h;if(!w(s))throw new TypeError(i("invalid argument. First argument must be an ndarray containing double-precision floating-point numbers. Value: `%s`.",s));if(!w(a))throw new TypeError(i("invalid argument. Second argument must be an ndarray containing double-precision floating-point numbers. Value: `%s`.",a));if(e=b(s),t=b(a),o=e.shape,n=t.shape,o.length<1)throw new TypeError(i("invalid argument. First argument must have at least one dimension."));if(n.length<1)throw new TypeError(i("invalid argument. Second argument must have at least one dimension."));if(arguments.length>2){if(r=arguments[2],!V(r))throw new TypeError(i("invalid argument. Third argument must be a negative integer. Value: `%s`.",r))}else r=-1;if(p=k(o.length,n.length)-1,r=j(r,p),r===-1)throw new RangeError(i("invalid argument. Third argument must be a value on the interval: [%d,%d]. Value: `%d`.",-p,-1,arguments[2]));if(d=o[r],n[r]!==d)throw new RangeError(i("invalid argument. The size of the contracted dimension must be the same for both input ndarrays. Dim(%s,%d) = %d. Dim(%s,%d) = %d.","x",r,d,"y",r,n[r]));try{f=z([e,t])}catch(B){throw new Error(i("invalid arguments. Input ndarrays must be broadcast compatible. Shape(%s) = (%s). Shape(%s) = (%s).","x",o.join(","),"y",n.join(",")))}if(e=f[0],t=f[1],v=F(e.shape,r),u=D(v,{dtype:e.dtype,order:e.order}),v.length===0)return g=x(d,e.data,e.strides[0],e.offset,t.data,t.strides[0],t.offset),u.iset(g),u;for(y=q(e,[r]),c=q(t,[r]),h=0;h\n*\n* var v = z.get();\n* // returns -5.0\n*/\nfunction ddot( x, y ) {\n\tvar dim;\n\tvar xsh;\n\tvar ysh;\n\tvar osh;\n\tvar xit;\n\tvar yit;\n\tvar out;\n\tvar tmp;\n\tvar xc;\n\tvar yc;\n\tvar vx;\n\tvar vy;\n\tvar dm;\n\tvar S;\n\tvar v;\n\tvar i;\n\n\tif ( !isFloat64ndarrayLike( x ) ) {\n\t\tthrow new TypeError( format( 'invalid argument. First argument must be an ndarray containing double-precision floating-point numbers. Value: `%s`.', x ) );\n\t}\n\tif ( !isFloat64ndarrayLike( y ) ) {\n\t\tthrow new TypeError( format( 'invalid argument. Second argument must be an ndarray containing double-precision floating-point numbers. Value: `%s`.', y ) );\n\t}\n\t// Convert the input arrays to \"base\" ndarrays:\n\txc = ndarraylike2ndarray( x );\n\tyc = ndarraylike2ndarray( y );\n\n\t// Resolve the input array shapes:\n\txsh = xc.shape;\n\tysh = yc.shape;\n\n\t// Validate that we've been provided non-zero-dimensional arrays...\n\tif ( xsh.length < 1 ) {\n\t\tthrow new TypeError( format( 'invalid argument. First argument must have at least one dimension.' ) );\n\t}\n\tif ( ysh.length < 1 ) {\n\t\tthrow new TypeError( format( 'invalid argument. Second argument must have at least one dimension.' ) );\n\t}\n\t// Validate that the dimension argument is a negative integer...\n\tif ( arguments.length > 2 ) {\n\t\tdim = arguments[ 2 ];\n\t\tif ( !isNegativeInteger( dim ) ) {\n\t\t\tthrow new TypeError( format( 'invalid argument. Third argument must be a negative integer. Value: `%s`.', dim ) );\n\t\t}\n\t} else {\n\t\tdim = -1;\n\t}\n\t// Validate that a provided dimension index is within bounds **before** broadcasting...\n\tdm = min( xsh.length, ysh.length ) - 1;\n\tdim = normalizeIndex( dim, dm );\n\tif ( dim === -1 ) {\n\t\tthrow new RangeError( format( 'invalid argument. Third argument must be a value on the interval: [%d,%d]. Value: `%d`.', -dm, -1, arguments[ 2 ] ) );\n\t}\n\t// Validate that the contracted dimension size is the same for both input arrays...\n\tS = xsh[ dim ];\n\tif ( ysh[ dim ] !== S ) {\n\t\tthrow new RangeError( format( 'invalid argument. The size of the contracted dimension must be the same for both input ndarrays. Dim(%s,%d) = %d. Dim(%s,%d) = %d.', 'x', dim, S, 'y', dim, ysh[ dim ] ) );\n\t}\n\t// Broadcast the input arrays to a common shape....\n\ttry {\n\t\ttmp = maybeBroadcastArrays( [ xc, yc ] );\n\t} catch ( err ) { // eslint-disable-line no-unused-vars\n\t\tthrow new Error( format( 'invalid arguments. Input ndarrays must be broadcast compatible. Shape(%s) = (%s). Shape(%s) = (%s).', 'x', xsh.join( ',' ), 'y', ysh.join( ',' ) ) );\n\t}\n\txc = tmp[ 0 ];\n\tyc = tmp[ 1 ];\n\n\t// Resolve the output array shape by excluding the contracted dimension:\n\tosh = without( xc.shape, dim );\n\n\t// Allocate an empty output array:\n\tout = empty( osh, {\n\t\t'dtype': xc.dtype,\n\t\t'order': xc.order\n\t});\n\n\t// If we are only provided one-dimensional input arrays, we can skip iterating over stacks...\n\tif ( osh.length === 0 ) {\n\t\tv = base( S, xc.data, xc.strides[0], xc.offset, yc.data, yc.strides[0], yc.offset ); // eslint-disable-line max-len\n\t\tout.iset( v );\n\t\treturn out;\n\t}\n\t// Create iterators for iterating over stacks of vectors:\n\txit = nditerStacks( xc, [ dim ] );\n\tyit = nditerStacks( yc, [ dim ] );\n\n\t// Compute the dot product for each pair of vectors...\n\tfor ( i = 0; i < numel( osh ); i++ ) {\n\t\tvx = xit.next().value;\n\t\tvy = yit.next().value;\n\t\tv = base( S, vx.data, vx.strides[0], vx.offset, vy.data, vy.strides[0], vy.offset ); // eslint-disable-line max-len\n\t\tout.iset( i, v );\n\t}\n\treturn out;\n}\n\n\n// EXPORTS //\n\nmodule.exports = ddot;\n", "/**\n* @license Apache-2.0\n*\n* Copyright (c) 2020 The Stdlib Authors.\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n'use strict';\n\n/**\n* BLAS level 1 routine to compute the dot product of two double-precision floating-point vectors.\n*\n* @module @stdlib/blas-ddot\n*\n* @example\n* var Float64Array = require( '@stdlib/array-float64' );\n* var array = require( '@stdlib/ndarray-array' );\n* var ddot = require( '@stdlib/blas-ddot' );\n*\n* var x = array( new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0 ] ) );\n* var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );\n*\n* var z = ddot( x, y );\n* // returns \n*\n* var v = z.get();\n* // returns -5.0\n*/\n\n// MODULES //\n\nvar main = require( './main.js' );\n\n\n// EXPORTS //\n\nmodule.exports = main;\n"],
+ "mappings": "uGAAA,IAAAA,EAAAC,EAAA,SAAAC,EAAAC,EAAA,cAsBA,IAAIC,EAAuB,QAAS,uCAAwC,EACxEC,EAAoB,QAAS,oCAAqC,EAAE,YACpEC,EAAM,QAAS,oCAAqC,EACpDC,EAAU,QAAS,4BAA6B,EAChDC,EAAQ,QAAS,4BAA6B,EAC9CC,EAAiB,QAAS,sCAAuC,EACjEC,EAAuB,QAAS,6CAA8C,EAC9EC,EAAsB,QAAS,0CAA2C,EAC1EC,EAAe,QAAS,6BAA8B,EACtDC,EAAQ,QAAS,uBAAwB,EACzCC,EAAO,QAAS,wBAAyB,EAAE,QAC3CC,EAAS,QAAS,uBAAwB,EAkC9C,SAASC,EAAMC,EAAGC,EAAI,CACrB,IAAIC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EAEJ,GAAK,CAAC9B,EAAsBa,CAAE,EAC7B,MAAM,IAAI,UAAWF,EAAQ,uHAAwHE,CAAE,CAAE,EAE1J,GAAK,CAACb,EAAsBc,CAAE,EAC7B,MAAM,IAAI,UAAWH,EAAQ,wHAAyHG,CAAE,CAAE,EAW3J,GARAS,EAAKhB,EAAqBM,CAAE,EAC5BW,EAAKjB,EAAqBO,CAAE,EAG5BE,EAAMO,EAAG,MACTN,EAAMO,EAAG,MAGJR,EAAI,OAAS,EACjB,MAAM,IAAI,UAAWL,EAAQ,oEAAqE,CAAE,EAErG,GAAKM,EAAI,OAAS,EACjB,MAAM,IAAI,UAAWN,EAAQ,qEAAsE,CAAE,EAGtG,GAAK,UAAU,OAAS,GAEvB,GADAI,EAAM,UAAW,CAAE,EACd,CAACd,EAAmBc,CAAI,EAC5B,MAAM,IAAI,UAAWJ,EAAQ,4EAA6EI,CAAI,CAAE,OAGjHA,EAAM,GAKP,GAFAY,EAAKzB,EAAKc,EAAI,OAAQC,EAAI,MAAO,EAAI,EACrCF,EAAMV,EAAgBU,EAAKY,CAAG,EACzBZ,IAAQ,GACZ,MAAM,IAAI,WAAYJ,EAAQ,0FAA2F,CAACgB,EAAI,GAAI,UAAW,CAAE,CAAE,CAAE,EAIpJ,GADAC,EAAIZ,EAAKD,CAAI,EACRE,EAAKF,CAAI,IAAMa,EACnB,MAAM,IAAI,WAAYjB,EAAQ,qIAAsI,IAAKI,EAAKa,EAAG,IAAKb,EAAKE,EAAKF,CAAI,CAAE,CAAE,EAGzM,GAAI,CACHO,EAAMhB,EAAsB,CAAEiB,EAAIC,CAAG,CAAE,CACxC,OAAUO,EAAM,CACf,MAAM,IAAI,MAAOpB,EAAQ,sGAAuG,IAAKK,EAAI,KAAM,GAAI,EAAG,IAAKC,EAAI,KAAM,GAAI,CAAE,CAAE,CAC9K,CAcA,GAbAM,EAAKD,EAAK,CAAE,EACZE,EAAKF,EAAK,CAAE,EAGZJ,EAAMf,EAASoB,EAAG,MAAOR,CAAI,EAG7BM,EAAMZ,EAAOS,EAAK,CACjB,MAASK,EAAG,MACZ,MAASA,EAAG,KACb,CAAC,EAGIL,EAAI,SAAW,EACnB,OAAAW,EAAInB,EAAMkB,EAAGL,EAAG,KAAMA,EAAG,QAAQ,CAAC,EAAGA,EAAG,OAAQC,EAAG,KAAMA,EAAG,QAAQ,CAAC,EAAGA,EAAG,MAAO,EAClFH,EAAI,KAAMQ,CAAE,EACLR,EAOR,IAJAF,EAAMX,EAAce,EAAI,CAAER,CAAI,CAAE,EAChCK,EAAMZ,EAAcgB,EAAI,CAAET,CAAI,CAAE,EAG1Be,EAAI,EAAGA,EAAI1B,EAAOc,CAAI,EAAGY,IAC9BL,EAAKN,EAAI,KAAK,EAAE,MAChBO,EAAKN,EAAI,KAAK,EAAE,MAChBS,EAAInB,EAAMkB,EAAGH,EAAG,KAAMA,EAAG,QAAQ,CAAC,EAAGA,EAAG,OAAQC,EAAG,KAAMA,EAAG,QAAQ,CAAC,EAAGA,EAAG,MAAO,EAClFL,EAAI,KAAMS,EAAGD,CAAE,EAEhB,OAAOR,CACR,CAKAtB,EAAO,QAAUa,IC7HjB,IAAIoB,EAAO,IAKX,OAAO,QAAUA",
+ "names": ["require_main", "__commonJSMin", "exports", "module", "isFloat64ndarrayLike", "isNegativeInteger", "min", "without", "numel", "normalizeIndex", "maybeBroadcastArrays", "ndarraylike2ndarray", "nditerStacks", "empty", "base", "format", "ddot", "x", "y", "dim", "xsh", "ysh", "osh", "xit", "yit", "out", "tmp", "xc", "yc", "vx", "vy", "dm", "S", "v", "i", "err", "main"]
}
diff --git a/docs/repl.txt b/docs/repl.txt
index cfd0d40..98a26ac 100644
--- a/docs/repl.txt
+++ b/docs/repl.txt
@@ -1,27 +1,52 @@
-{{alias}}( x, y )
+{{alias}}( x, y[, dim] )
Computes the dot product of two double-precision floating-point vectors.
- If provided empty vectors, the function returns `0.0`.
+ If provided at least one input array having more than one dimension, the
+ input arrays are broadcasted to a common shape.
+
+ For multi-dimensional input arrays, the function performs batched
+ computation, such that the function computes the dot product for each pair
+ of vectors in `x` and `y` according to the specified dimension index.
+
+ The size of the contracted dimension must be the same for both input arrays.
+
+ The function resolves the dimension index for which to compute the dot
+ product *before* broadcasting.
+
+ If provided empty vectors, the dot product is `0`.
Parameters
----------
x: ndarray
- First input array whose underlying data type is 'float64'.
+ First input array. Must have a 'float64' data type. Must have at least
+ one dimension and be broadcast-compatible with the second input array.
y: ndarray
- Second input array whose underlying data type is 'float64'.
+ Second input array. Must have a 'float64' data type. Must have at least
+ one dimension and be broadcast-compatible with the first input array.
+
+ dim: integer (optional)
+ Dimension index for which to compute the dot product. Must be a negative
+ integer. Negative indices are resolved relative to the last array
+ dimension, with the last dimension corresponding to `-1`. Default: -1.
Returns
-------
- dot: number
- The dot product.
+ out: ndarray
+ The dot product. The output array has the same data type as the input
+ arrays and has a shape which is determined by broadcasting and excludes
+ the contracted dimension.
Examples
--------
- > var x = {{alias:@stdlib/ndarray/array}}( new {{alias:@stdlib/array/float64}}( [ 4.0, 2.0, -3.0, 5.0, -1.0 ] ) );
- > var y = {{alias:@stdlib/ndarray/array}}( new {{alias:@stdlib/array/float64}}( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );
- > {{alias}}( x, y )
+ > var xbuf = new {{alias:@stdlib/array/float64}}( [ 4.0, 2.0, -3.0, 5.0, -1.0 ] );
+ > var x = {{alias:@stdlib/ndarray/array}}( xbuf );
+ > var ybuf = new {{alias:@stdlib/array/float64}}( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] );
+ > var y = {{alias:@stdlib/ndarray/array}}( ybuf );
+ > var z = {{alias}}( x, y )
+
+ > z.get()
-5.0
See Also
diff --git a/docs/types/index.d.ts b/docs/types/index.d.ts
index c2f2ffc..d92b8a4 100644
--- a/docs/types/index.d.ts
+++ b/docs/types/index.d.ts
@@ -25,11 +25,23 @@ import { float64ndarray } from '@stdlib/types/ndarray';
/**
* Computes the dot product of two double-precision floating-point vectors.
*
+* ## Notes
+*
+* - If provided at least one input array having more than one dimension, the input arrays are broadcasted to a common shape.
+* - For multi-dimensional input arrays, the function performs batched computation, such that the function computes the dot product for each pair of vectors in `x` and `y` according to the specified dimension index.
+* - The size of the contracted dimension must be the same for both input arrays.
+* - The function resolves the dimension index for which to compute the dot product **before** broadcasting.
+* - If provided empty vectors, the dot product is `0`.
+* - Negative indices are resolved relative to the last array dimension, with the last dimension corresponding to `-1`.
+* - The output array has the same data type as the input arrays and has a shape which is determined by broadcasting and excludes the contracted dimension.
+*
* @param x - first input array
* @param y - second input array
-* @throws first argument must be a 1-dimensional `ndarray` containing double-precision floating-point numbers
-* @throws second argument must be a 1-dimensional `ndarray` containing double-precision floating-point numbers
-* @throws input arrays must be the same length
+* @param dim - dimension for which to compute the dot product (default: -1)
+* @throws first argument must be a non-zero-dimensional ndarray containing double-precision floating-point numbers
+* @throws second argument must be a non-zero-dimensional ndarray containing double-precision floating-point numbers
+* @throws input arrays must be broadcast-compatible
+* @throws the size of the contracted dimension must be the same for both input arrays
* @returns dot product
*
* @example
@@ -40,9 +52,12 @@ import { float64ndarray } from '@stdlib/types/ndarray';
* var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );
*
* var z = ddot( x, y );
+* returns
+*
+* var v = z.get();
* // returns -5.0
*/
-declare function ddot( x: float64ndarray, y: float64ndarray ): number;
+declare function ddot( x: float64ndarray, y: float64ndarray, dim?: number ): float64ndarray;
// EXPORTS //
diff --git a/docs/types/test.ts b/docs/types/test.ts
index 81984ba..b5874fe 100644
--- a/docs/types/test.ts
+++ b/docs/types/test.ts
@@ -24,7 +24,7 @@ import ddot = require( './index' );
// The function returns a number...
{
- ddot( zeros( [ 10 ] ), zeros( [ 10 ] ) ); // $ExpectType number
+ ddot( zeros( [ 10 ] ), zeros( [ 10 ] ) ); // $ExpectType float64ndarray
}
// The compiler throws an error if the function is provided a first argument which is not an ndarray...
@@ -40,6 +40,16 @@ import ddot = require( './index' );
ddot( {}, y ); // $ExpectError
ddot( [], y ); // $ExpectError
ddot( ( x: number ): number => x, y ); // $ExpectError
+
+ ddot( 10, y, -1 ); // $ExpectError
+ ddot( '10', y, -1 ); // $ExpectError
+ ddot( true, y, -1 ); // $ExpectError
+ ddot( false, y, -1 ); // $ExpectError
+ ddot( null, y, -1 ); // $ExpectError
+ ddot( undefined, y, -1 ); // $ExpectError
+ ddot( {}, y, -1 ); // $ExpectError
+ ddot( [], y, -1 ); // $ExpectError
+ ddot( ( x: number ): number => x, y, -1 ); // $ExpectError
}
// The compiler throws an error if the function is provided a second argument which is not an ndarray...
@@ -55,6 +65,30 @@ import ddot = require( './index' );
ddot( x, {} ); // $ExpectError
ddot( x, [] ); // $ExpectError
ddot( x, ( x: number ): number => x ); // $ExpectError
+
+ ddot( x, 10, -1 ); // $ExpectError
+ ddot( x, '10', -1 ); // $ExpectError
+ ddot( x, true, -1 ); // $ExpectError
+ ddot( x, false, -1 ); // $ExpectError
+ ddot( x, null, -1 ); // $ExpectError
+ ddot( x, undefined, -1 ); // $ExpectError
+ ddot( x, {}, -1 ); // $ExpectError
+ ddot( x, [], -1 ); // $ExpectError
+ ddot( x, ( x: number ): number => x, -1 ); // $ExpectError
+}
+
+// The compiler throws an error if the function is provided a third argument which is not a number...
+{
+ const x = zeros( [ 10 ] );
+ const y = zeros( [ 10 ] );
+
+ ddot( x, y, '10' ); // $ExpectError
+ ddot( x, y, true ); // $ExpectError
+ ddot( x, y, false ); // $ExpectError
+ ddot( x, y, null ); // $ExpectError
+ ddot( x, y, {} ); // $ExpectError
+ ddot( x, y, [] ); // $ExpectError
+ ddot( x, y, ( x: number ): number => x ); // $ExpectError
}
// The compiler throws an error if the function is provided an unsupported number of arguments...
@@ -64,5 +98,5 @@ import ddot = require( './index' );
ddot(); // $ExpectError
ddot( x ); // $ExpectError
- ddot( x, y, {} ); // $ExpectError
+ ddot( x, y, -1, {} ); // $ExpectError
}
diff --git a/examples/index.js b/examples/index.js
index 706ff78..17c1a16 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -18,24 +18,24 @@
'use strict';
-var discreteUniform = require( '@stdlib/random-base-discrete-uniform' );
-var Float64Array = require( '@stdlib/array-float64' );
+var discreteUniform = require( '@stdlib/random-array-discrete-uniform' );
+var ndarray2array = require( '@stdlib/ndarray-to-array' );
var array = require( '@stdlib/ndarray-array' );
var ddot = require( './../lib' );
-var x = array( new Float64Array( 10 ) );
-var y = array( new Float64Array( 10 ) );
+var opts = {
+ 'dtype': 'float64'
+};
-var rand1 = discreteUniform.factory( 0, 100 );
-var rand2 = discreteUniform.factory( 0, 10 );
+var x = array( discreteUniform( 10, 0, 100, opts ), {
+ 'shape': [ 5, 2 ]
+});
+console.log( ndarray2array( x ) );
-var i;
-for ( i = 0; i < x.length; i++ ) {
- x.set( i, rand1() );
- y.set( i, rand2() );
-}
-console.log( x.toString() );
-console.log( y.toString() );
+var y = array( discreteUniform( 10, 0, 10, opts ), {
+ 'shape': x.shape
+});
+console.log( ndarray2array( y ) );
-var z = ddot( x, y );
-console.log( z );
+var z = ddot( x, y, -1 );
+console.log( ndarray2array( z ) );
diff --git a/lib/index.js b/lib/index.js
index ae9a502..ed9dfa8 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -32,6 +32,9 @@
* var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );
*
* var z = ddot( x, y );
+* // returns
+*
+* var v = z.get();
* // returns -5.0
*/
diff --git a/lib/main.js b/lib/main.js
index 44f715d..3d03cd8 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -20,9 +20,18 @@
// MODULES //
-var isFloat64VectorLike = require( '@stdlib/assert-is-float64vector-like' );
+var isFloat64ndarrayLike = require( '@stdlib/assert-is-float64ndarray-like' );
+var isNegativeInteger = require( '@stdlib/assert-is-negative-integer' ).isPrimitive;
+var min = require( '@stdlib/math-base-special-fast-min' );
+var without = require( '@stdlib/array-base-without' );
+var numel = require( '@stdlib/ndarray-base-numel' );
+var normalizeIndex = require( '@stdlib/ndarray-base-normalize-index' );
+var maybeBroadcastArrays = require( '@stdlib/ndarray-base-maybe-broadcast-arrays' );
+var ndarraylike2ndarray = require( '@stdlib/ndarray-base-ndarraylike2ndarray' );
+var nditerStacks = require( '@stdlib/ndarray-iter-stacks' );
+var empty = require( '@stdlib/ndarray-empty' );
+var base = require( '@stdlib/blas-base-ddot' ).ndarray;
var format = require( '@stdlib/string-format' );
-var dot = require( '@stdlib/blas-base-ddot' ).ndarray;
// MAIN //
@@ -30,12 +39,18 @@ var dot = require( '@stdlib/blas-base-ddot' ).ndarray;
/**
* Computes the dot product of two double-precision floating-point vectors.
*
-* @param {VectorLike} x - first input array
-* @param {VectorLike} y - second input array
-* @throws {TypeError} first argument must be a 1-dimensional ndarray containing double-precision floating-point numbers
-* @throws {TypeError} second argument must be a 1-dimensional ndarray containing double-precision floating-point numbers
-* @throws {RangeError} input arrays must be the same length
-* @returns {number} dot product
+* @param {ndarrayLike} x - first input array
+* @param {ndarrayLike} y - second input array
+* @param {NegativeInteger} dim - dimension for which to compute the dot product
+* @throws {TypeError} first argument must be a ndarray containing double-precision floating-point numbers
+* @throws {TypeError} first argument must have at least one dimension
+* @throws {TypeError} second argument must be a ndarray containing double-precision floating-point numbers
+* @throws {TypeError} second argument must have at least one dimension
+* @throws {TypeError} third argument must be a negative integer
+* @throws {Error} input arrays must be broadcast compatible
+* @throws {RangeError} the size of the contracted dimension must be the same for both input arrays
+* @throws {RangeError} third argument is out-of-bounds
+* @returns {ndarray} ndarray containing the dot product
*
* @example
* var Float64Array = require( '@stdlib/array-float64' );
@@ -45,19 +60,106 @@ var dot = require( '@stdlib/blas-base-ddot' ).ndarray;
* var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );
*
* var z = ddot( x, y );
+* // returns
+*
+* var v = z.get();
* // returns -5.0
*/
function ddot( x, y ) {
- if ( !isFloat64VectorLike( x ) ) {
- throw new TypeError( format( 'invalid argument. First argument must be a one-dimensional ndarray containing double-precision floating-point numbers (i.e., an ndarray whose underlying data buffer is a Float64Array). Value: `%s`.', x ) );
+ var dim;
+ var xsh;
+ var ysh;
+ var osh;
+ var xit;
+ var yit;
+ var out;
+ var tmp;
+ var xc;
+ var yc;
+ var vx;
+ var vy;
+ var dm;
+ var S;
+ var v;
+ var i;
+
+ if ( !isFloat64ndarrayLike( x ) ) {
+ throw new TypeError( format( 'invalid argument. First argument must be an ndarray containing double-precision floating-point numbers. Value: `%s`.', x ) );
+ }
+ if ( !isFloat64ndarrayLike( y ) ) {
+ throw new TypeError( format( 'invalid argument. Second argument must be an ndarray containing double-precision floating-point numbers. Value: `%s`.', y ) );
+ }
+ // Convert the input arrays to "base" ndarrays:
+ xc = ndarraylike2ndarray( x );
+ yc = ndarraylike2ndarray( y );
+
+ // Resolve the input array shapes:
+ xsh = xc.shape;
+ ysh = yc.shape;
+
+ // Validate that we've been provided non-zero-dimensional arrays...
+ if ( xsh.length < 1 ) {
+ throw new TypeError( format( 'invalid argument. First argument must have at least one dimension.' ) );
+ }
+ if ( ysh.length < 1 ) {
+ throw new TypeError( format( 'invalid argument. Second argument must have at least one dimension.' ) );
+ }
+ // Validate that the dimension argument is a negative integer...
+ if ( arguments.length > 2 ) {
+ dim = arguments[ 2 ];
+ if ( !isNegativeInteger( dim ) ) {
+ throw new TypeError( format( 'invalid argument. Third argument must be a negative integer. Value: `%s`.', dim ) );
+ }
+ } else {
+ dim = -1;
}
- if ( !isFloat64VectorLike( y ) ) {
- throw new TypeError( format( 'invalid argument. Second argument must be a one-dimensional ndarray containing double-precision floating-point numbers (i.e., an ndarray whose underlying data buffer is a Float64Array). Value: `%s`.', y ) );
+ // Validate that a provided dimension index is within bounds **before** broadcasting...
+ dm = min( xsh.length, ysh.length ) - 1;
+ dim = normalizeIndex( dim, dm );
+ if ( dim === -1 ) {
+ throw new RangeError( format( 'invalid argument. Third argument must be a value on the interval: [%d,%d]. Value: `%d`.', -dm, -1, arguments[ 2 ] ) );
}
- if ( x.length !== y.length ) {
- throw new RangeError( format( 'invalid argument. Arrays must be the same length. First argument length: `%u`. Second argument length: `%u`.', x.length, y.length ) );
+ // Validate that the contracted dimension size is the same for both input arrays...
+ S = xsh[ dim ];
+ if ( ysh[ dim ] !== S ) {
+ throw new RangeError( format( 'invalid argument. The size of the contracted dimension must be the same for both input ndarrays. Dim(%s,%d) = %d. Dim(%s,%d) = %d.', 'x', dim, S, 'y', dim, ysh[ dim ] ) );
+ }
+ // Broadcast the input arrays to a common shape....
+ try {
+ tmp = maybeBroadcastArrays( [ xc, yc ] );
+ } catch ( err ) { // eslint-disable-line no-unused-vars
+ throw new Error( format( 'invalid arguments. Input ndarrays must be broadcast compatible. Shape(%s) = (%s). Shape(%s) = (%s).', 'x', xsh.join( ',' ), 'y', ysh.join( ',' ) ) );
+ }
+ xc = tmp[ 0 ];
+ yc = tmp[ 1 ];
+
+ // Resolve the output array shape by excluding the contracted dimension:
+ osh = without( xc.shape, dim );
+
+ // Allocate an empty output array:
+ out = empty( osh, {
+ 'dtype': xc.dtype,
+ 'order': xc.order
+ });
+
+ // If we are only provided one-dimensional input arrays, we can skip iterating over stacks...
+ if ( osh.length === 0 ) {
+ v = base( S, xc.data, xc.strides[0], xc.offset, yc.data, yc.strides[0], yc.offset ); // eslint-disable-line max-len
+ out.iset( v );
+ return out;
+ }
+ // Create iterators for iterating over stacks of vectors:
+ xit = nditerStacks( xc, [ dim ] );
+ yit = nditerStacks( yc, [ dim ] );
+
+ // Compute the dot product for each pair of vectors...
+ for ( i = 0; i < numel( osh ); i++ ) {
+ vx = xit.next().value;
+ vy = yit.next().value;
+ v = base( S, vx.data, vx.strides[0], vx.offset, vy.data, vy.strides[0], vy.offset ); // eslint-disable-line max-len
+ out.iset( i, v );
}
- return dot( x.length, x.data, x.strides[ 0 ], x.offset, y.data, y.strides[ 0 ], y.offset ); // eslint-disable-line max-len
+ return out;
}
diff --git a/package.json b/package.json
index fbb877c..351b973 100644
--- a/package.json
+++ b/package.json
@@ -38,8 +38,17 @@
"url": "https://github.com/stdlib-js/stdlib/issues"
},
"dependencies": {
- "@stdlib/assert-is-float64vector-like": "^0.2.2",
+ "@stdlib/array-base-without": "github:stdlib-js/array-base-without#main",
+ "@stdlib/assert-is-float64ndarray-like": "^0.2.2",
+ "@stdlib/assert-is-negative-integer": "^0.2.2",
"@stdlib/blas-base-ddot": "^0.3.0",
+ "@stdlib/math-base-special-fast-min": "^0.3.0",
+ "@stdlib/ndarray-base-maybe-broadcast-arrays": "^0.2.1",
+ "@stdlib/ndarray-base-ndarraylike2ndarray": "github:stdlib-js/ndarray-base-ndarraylike2ndarray#main",
+ "@stdlib/ndarray-base-normalize-index": "^0.2.2",
+ "@stdlib/ndarray-base-numel": "^0.2.2",
+ "@stdlib/ndarray-empty": "^0.3.0",
+ "@stdlib/ndarray-iter-stacks": "github:stdlib-js/ndarray-iter-stacks#main",
"@stdlib/string-format": "^0.2.2",
"@stdlib/types": "^0.3.2",
"@stdlib/error-tools-fmtprodmsg": "^0.2.2"
@@ -51,8 +60,12 @@
"@stdlib/math-base-special-pow": "^0.3.0",
"@stdlib/ndarray-array": "^0.2.1",
"@stdlib/ndarray-ctor": "^0.2.2",
- "@stdlib/random-base-discrete-uniform": "^0.2.1",
- "@stdlib/random-base-randu": "^0.2.1",
+ "@stdlib/ndarray-ndims": "^0.2.2",
+ "@stdlib/ndarray-shape": "^0.2.2",
+ "@stdlib/ndarray-to-array": "^0.2.1",
+ "@stdlib/ndarray-zeros": "^0.3.0",
+ "@stdlib/random-array-discrete-uniform": "^0.2.1",
+ "@stdlib/random-array-uniform": "^0.2.1",
"tape": "git+https://github.com/kgryte/tape.git#fix/globby",
"istanbul": "^0.4.1",
"tap-min": "git+https://github.com/Planeshifter/tap-min.git",
diff --git a/test/test.js b/test/test.js
index 513ebf7..f94457a 100644
--- a/test/test.js
+++ b/test/test.js
@@ -24,7 +24,10 @@ var tape = require( 'tape' );
var Float64Array = require( '@stdlib/array-float64' );
var Float32Array = require( '@stdlib/array-float32' );
var array = require( '@stdlib/ndarray-array' );
+var zeros = require( '@stdlib/ndarray-zeros' );
var ndarray = require( '@stdlib/ndarray-ctor' );
+var ndims = require( '@stdlib/ndarray-ndims' );
+var shape = require( '@stdlib/ndarray-shape' );
var ddot = require( './../lib' );
@@ -41,7 +44,7 @@ tape( 'the function has an arity of 2', function test( t ) {
t.end();
});
-tape( 'the function throws an error if provided a first argument which is not a 1-dimensional ndarray containing double-precision floating-point numbers', function test( t ) {
+tape( 'the function throws an error if provided a first argument which is not a non-zero-dimensional ndarray containing double-precision floating-point numbers', function test( t ) {
var values;
var i;
@@ -55,6 +58,9 @@ tape( 'the function throws an error if provided a first argument which is not a
{},
[],
function noop() {},
+ zeros( [], {
+ 'dtype': 'float64'
+ }),
array( new Float32Array( 10 ) )
];
@@ -72,7 +78,7 @@ tape( 'the function throws an error if provided a first argument which is not a
}
});
-tape( 'the function throws an error if provided a second argument which is not a 1-dimensional ndarray containing double-precision floating-point numbers', function test( t ) {
+tape( 'the function throws an error if provided a first argument which is not a non-zero-dimensional ndarray containing double-precision floating-point numbers (dimension)', function test( t ) {
var values;
var i;
@@ -86,6 +92,43 @@ tape( 'the function throws an error if provided a second argument which is not a
{},
[],
function noop() {},
+ zeros( [], {
+ 'dtype': 'float64'
+ }),
+ array( new Float32Array( 10 ) )
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ var y = array( new Float64Array( 10 ) );
+
+ return function badValue() {
+ ddot( value, y, -1 );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided a second argument which is not a non-zero-dimensional ndarray containing double-precision floating-point numbers', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ 5,
+ '5',
+ true,
+ false,
+ null,
+ void 0,
+ {},
+ [],
+ function noop() {},
+ zeros( [], {
+ 'dtype': 'float64'
+ }),
array( new Float32Array( 10 ) )
];
@@ -103,7 +146,167 @@ tape( 'the function throws an error if provided a second argument which is not a
}
});
-tape( 'the function throws an error if provided unequal length vectors', function test( t ) {
+tape( 'the function throws an error if provided a second argument which is not a non-zero-dimensional ndarray containing double-precision floating-point numbers (dimension)', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ 5,
+ '5',
+ true,
+ false,
+ null,
+ void 0,
+ {},
+ [],
+ function noop() {},
+ zeros( [], {
+ 'dtype': 'float64'
+ }),
+ array( new Float32Array( 10 ) )
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ var x = array( new Float64Array( 10 ) );
+
+ return function badValue() {
+ ddot( x, value, -1 );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided a third argument which is not a negative integer (vectors)', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ '5',
+ 0,
+ 5,
+ NaN,
+ -3.14,
+ true,
+ false,
+ null,
+ void 0,
+ {},
+ [],
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ var x = array( new Float64Array( 10 ) );
+ var y = array( new Float64Array( 10 ) );
+
+ return function badValue() {
+ ddot( x, y, value );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided a third argument which is not a negative integer (multi-dimensional arrays)', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ '5',
+ 0,
+ 5,
+ NaN,
+ -3.14,
+ true,
+ false,
+ null,
+ void 0,
+ {},
+ [],
+ function noop() {}
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ var opts = {
+ 'shape': [ 2, 5 ]
+ };
+ var x = array( new Float64Array( 10 ), opts );
+ var y = array( new Float64Array( 10 ), opts );
+
+ return function badValue() {
+ ddot( x, y, value );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided a third argument which is out-of-bounds (vectors)', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ -2,
+ -3,
+ -4,
+ -5
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ var x = array( new Float64Array( 10 ) );
+ var y = array( new Float64Array( 10 ) );
+
+ return function badValue() {
+ ddot( x, y, value );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided a third argument which is out-of-bounds (multi-dimensional arrays)', function test( t ) {
+ var values;
+ var i;
+
+ values = [
+ -3,
+ -4,
+ -5,
+ -10
+ ];
+
+ for ( i = 0; i < values.length; i++ ) {
+ t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] );
+ }
+ t.end();
+
+ function badValue( value ) {
+ var opts = {
+ 'shape': [ 2, 5 ]
+ };
+ var x = array( new Float64Array( 10 ), opts );
+ var y = array( new Float64Array( 10 ), opts );
+
+ return function badValue() {
+ ddot( x, y, value );
+ };
+ }
+});
+
+tape( 'the function throws an error if provided arrays having an unequal contracted dimension (vectors)', function test( t ) {
t.throws( badValue, RangeError, 'throws an error' );
t.end();
@@ -113,6 +316,37 @@ tape( 'the function throws an error if provided unequal length vectors', functio
ddot( x, y );
}
});
+
+tape( 'the function throws an error if provided arrays having an unequal contracted dimension (multi-dimensional)', function test( t ) {
+ t.throws( badValue, RangeError, 'throws an error' );
+ t.end();
+
+ function badValue() {
+ var x = array( new Float64Array( 100 ), {
+ 'shape': [ 25, 4 ]
+ });
+ var y = array( new Float64Array( 100 ), {
+ 'shape': [ 50, 2 ]
+ });
+ ddot( x, y, -1 );
+ }
+});
+
+tape( 'the function throws an error if provided broadcast-incompatible input arrays', function test( t ) {
+ t.throws( badValue, Error, 'throws an error' );
+ t.end();
+
+ function badValue() {
+ var x = array( new Float64Array( 100 ), {
+ 'shape': [ 5, 10, 2 ]
+ });
+ var y = array( new Float64Array( 100 ), {
+ 'shape': [ 50, 1, 2 ]
+ });
+ ddot( x, y, -1 );
+ }
+});
+
tape( 'the function calculates the dot product of vectors `x` and `y`', function test( t ) {
var dot;
var x;
@@ -125,12 +359,132 @@ tape( 'the function calculates the dot product of vectors `x` and `y`', function
y = array( y );
dot = ddot( x, y );
- t.strictEqual( dot, -17.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), -17.0, 'returns expected value' );
+
+ t.end();
+});
+
+tape( 'the function supports operating on stacks of vectors (default)', function test( t ) {
+ var opts;
+ var dot;
+ var x;
+ var y;
+
+ x = new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0, 2.0, -5.0, 6.0 ] );
+ y = new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0, 8.0, 2.0, -3.0 ] );
+
+ opts = {
+ 'shape': [ 4, 2 ]
+ };
+ x = array( x, opts );
+ y = array( y, opts );
+
+ dot = ddot( x, y );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [ 4 ], 'returns expected value' );
+ t.strictEqual( dot.get( 0 ), 20.0, 'returns expected value' );
+ t.strictEqual( dot.get( 1 ), -17.0, 'returns expected value' );
+ t.strictEqual( dot.get( 2 ), 8.0, 'returns expected value' );
+ t.strictEqual( dot.get( 3 ), -28.0, 'returns expected value' );
+
+ t.end();
+});
+
+tape( 'the function supports operating on stacks of vectors (dim=-1)', function test( t ) {
+ var opts;
+ var dot;
+ var x;
+ var y;
+
+ x = new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0, 2.0, -5.0, 6.0 ] );
+ y = new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0, 8.0, 2.0, -3.0 ] );
+
+ opts = {
+ 'shape': [ 4, 2 ]
+ };
+ x = array( x, opts );
+ y = array( y, opts );
+
+ dot = ddot( x, y, -1 );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [ 4 ], 'returns expected value' );
+ t.strictEqual( dot.get( 0 ), 20.0, 'returns expected value' );
+ t.strictEqual( dot.get( 1 ), -17.0, 'returns expected value' );
+ t.strictEqual( dot.get( 2 ), 8.0, 'returns expected value' );
+ t.strictEqual( dot.get( 3 ), -28.0, 'returns expected value' );
+
+ t.end();
+});
+
+tape( 'the function supports operating on stacks of vectors (dim=-2)', function test( t ) {
+ var opts;
+ var dot;
+ var x;
+ var y;
+
+ x = new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0, 2.0, -5.0, 6.0 ] );
+ y = new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0, 8.0, 2.0, -3.0 ] );
+
+ opts = {
+ 'shape': [ 2, 4 ]
+ };
+ x = array( x, opts );
+ y = array( y, opts );
+
+ dot = ddot( x, y, -2 );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [ 4 ], 'returns expected value' );
+ t.strictEqual( dot.get( 0 ), 0.0, 'returns expected value' );
+ t.strictEqual( dot.get( 1 ), 28.0, 'returns expected value' );
+ t.strictEqual( dot.get( 2 ), -7.0, 'returns expected value' );
+ t.strictEqual( dot.get( 3 ), -38.0, 'returns expected value' );
+
+ t.end();
+});
+
+tape( 'the function supports broadcasting', function test( t ) {
+ var opts;
+ var dot;
+ var x;
+ var y;
+
+ x = new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0, 2.0, -5.0, 6.0 ] );
+ y = new Float64Array( [ 2.0, 6.0 ] );
+
+ opts = {
+ 'shape': [ 4, 2 ]
+ };
+ x = array( x, opts );
+
+ opts = {
+ 'shape': [ 1, 2 ]
+ };
+ y = array( y, opts );
+
+ dot = ddot( x, y, -1 );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [ 4 ], 'returns expected value' );
+ t.strictEqual( dot.get( 0 ), 20.0, 'returns expected value' );
+ t.strictEqual( dot.get( 1 ), 24.0, 'returns expected value' );
+ t.strictEqual( dot.get( 2 ), 10.0, 'returns expected value' );
+ t.strictEqual( dot.get( 3 ), 26.0, 'returns expected value' );
t.end();
});
-tape( 'if provided empty vectors, the function returns `0`', function test( t ) {
+tape( 'if provided empty vectors, the dot product is `0`', function test( t ) {
var dot;
var x;
var y;
@@ -142,7 +496,11 @@ tape( 'if provided empty vectors, the function returns `0`', function test( t )
y = array( y );
dot = ddot( x, y );
- t.strictEqual( dot, 0.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), 0.0, 'returns expected value' );
t.end();
});
@@ -169,7 +527,11 @@ tape( 'the function supports a strided vector for the first argument', function
y = ndarray( 'float64', y, [ 3 ], [ 1 ], 0, 'row-major' );
dot = ddot( x, y );
- t.strictEqual( dot, -12.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), -12.0, 'returns expected value' );
t.end();
});
@@ -197,7 +559,11 @@ tape( 'the function supports a strided vector for the second argument', function
y = ndarray( 'float64', y, [ 3 ], [ 2 ], 0, 'row-major' );
dot = ddot( x, y );
- t.strictEqual( dot, 45.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), 45.0, 'returns expected value' );
t.end();
});
@@ -224,7 +590,11 @@ tape( 'the function supports negative strides', function test( t ) {
y = ndarray( 'float64', y, [ 3 ], [ -1 ], y.length-1, 'row-major' );
dot = ddot( x, y );
- t.strictEqual( dot, 67.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), 67.0, 'returns expected value' );
t.end();
});
@@ -251,7 +621,11 @@ tape( 'the function supports complex access patterns', function test( t ) {
y = ndarray( 'float64', y, [ 3 ], [ -1 ], y.length-1, 'row-major' );
dot = ddot( x, y );
- t.strictEqual( dot, 59.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), 59.0, 'returns expected value' );
t.end();
});
@@ -281,7 +655,11 @@ tape( 'the function supports strided vectors having offsets', function test( t )
y = ndarray( 'float64', y, [ 3 ], [ 1 ], 2, 'row-major' );
dot = ddot( x, y );
- t.strictEqual( dot, -12.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), -12.0, 'returns expected value' );
t.end();
});
@@ -317,7 +695,11 @@ tape( 'the function supports underlying data buffers with view offsets', functio
y1 = ndarray( 'float64', y1, [ 3 ], [ 1 ], 0, 'row-major' );
dot = ddot( x1, y1 );
- t.strictEqual( dot, 124.0, 'returns expected value' );
+
+ t.strictEqual( dot instanceof ndarray, true, 'returns expected value' );
+ t.strictEqual( ndims( dot ), ndims( x1 )-1, 'returns expected value' );
+ t.deepEqual( shape( dot ), [], 'returns expected value' );
+ t.strictEqual( dot.get(), 124.0, 'returns expected value' );
t.end();
});