Skip to content

Commit c4b89f1

Browse files
committed
refactor: add function to resolve a reshape strategy
--- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed ---
1 parent 6de44d2 commit c4b89f1

File tree

1 file changed

+260
-0
lines changed

1 file changed

+260
-0
lines changed
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var iterationOrder = require( '@stdlib/ndarray/base/iteration-order' );
24+
var minmaxViewBufferIndex = require( '@stdlib/ndarray/base/minmax-view-buffer-index' ).assign;
25+
var ndarraylike2object = require( '@stdlib/ndarray/base/ndarraylike2object' );
26+
var assign = require( '@stdlib/ndarray/base/assign' );
27+
var ndarraylike2ndarray = require( '@stdlib/ndarray/base/ndarraylike2ndarray' );
28+
var emptyLike = require( '@stdlib/ndarray/base/empty-like' );
29+
30+
31+
// FUNCTIONS //
32+
33+
/**
34+
* Returns an input ndarray.
35+
*
36+
* @private
37+
* @param {ndarrayLike} x - input ndarray
38+
* @returns {ndarrayLike} input ndarray
39+
*/
40+
function identity( x ) {
41+
return x;
42+
}
43+
44+
/**
45+
* Broadcasts a zero-dimensional ndarray to a one-dimensional ndarray view containing a single element.
46+
*
47+
* @private
48+
* @param {ndarrayLike} x - input ndarray
49+
* @returns {ndarrayLike} broadcasted ndarray view
50+
*/
51+
function broadcast( x ) {
52+
// NOTE: the following properties must be set in the exact same order as in `x` in order to ensure that the returned object has the same hidden shape as the input ndarray-like object...
53+
return {
54+
'dtype': x.dtype,
55+
'data': x.data,
56+
'shape': [ 1 ],
57+
'strides': [ 0 ],
58+
'offset': x.offset,
59+
'order': x.order
60+
};
61+
}
62+
63+
/**
64+
* Returns a function which returns an ndarray view in which the singleton dimensions are removed from an input ndarray having only a single non-singleton dimension.
65+
*
66+
* @private
67+
* @param {ndarrayLike} arr - original ndarray
68+
* @param {NonNegativeInteger} index - index of the non-singleton dimension
69+
* @returns {Function} function for returning an ndarray view
70+
*/
71+
function squeeze( arr, index ) {
72+
var sh = [ arr.shape[ index ] ];
73+
var sx = [ arr.strides[ index ] ];
74+
return reshape;
75+
76+
/**
77+
* Returns an ndarray view in which the singleton dimensions are removed from an input ndarray having only a single non-singleton dimension.
78+
*
79+
* @private
80+
* @param {ndarrayLike} x - input ndarray
81+
* @returns {ndarrayLike} a squeezed ndarray view
82+
*/
83+
function reshape( x ) {
84+
// NOTE: the following properties must be set in the exact same order as in `arr` in order to ensure that the returned object has the same hidden shape as the input ndarray-like object...
85+
return {
86+
'dtype': x.dtype,
87+
'data': x.data,
88+
'shape': sh,
89+
'strides': sx,
90+
'offset': x.offset,
91+
'order': x.order
92+
};
93+
}
94+
}
95+
96+
/**
97+
* Returns a function which returns a one-dimensional ndarray view of a contiguous input ndarray having more than one dimension.
98+
*
99+
* @private
100+
* @param {NonNegativeInteger} len - number of elements in an ndarray
101+
* @param {integer} iox - iteration order
102+
* @returns {Function} function for returning a one-dimensional ndarray view
103+
*/
104+
function contiguous( len, iox ) {
105+
var xmmv;
106+
var ind;
107+
var sh;
108+
var sx;
109+
110+
// Resolve the index of the min/max view buffer element which is the first indexed element...
111+
if ( iox === 1 ) {
112+
ind = 0;
113+
} else {
114+
ind = 1;
115+
}
116+
// Initialize an array for storing the min/max view buffer elements:
117+
xmmv = [ 0, 0 ]; // [ min, max ]
118+
119+
// Initialize the output one-dimensional view's shape and strides:
120+
sh = [ len ];
121+
sx = [ iox ];
122+
123+
return reshape;
124+
125+
/**
126+
* Returns a one-dimensional ndarray view of a contiguous input ndarray having more than one dimension.
127+
*
128+
* @private
129+
* @param {ndarrayLike} x - input ndarray
130+
* @returns {ndarrayLike} a one-dimensional ndarray view
131+
*/
132+
function reshape( x ) {
133+
// Resolve the minimum and maximum linear indices in the underlying data buffer which are accessible to the input ndarray view:
134+
minmaxViewBufferIndex( x.shape, x.strides, x.offset, xmmv );
135+
136+
// NOTE: the following properties must be set in the exact same order as in `x` in order to ensure that the returned object has the same hidden shape as the input ndarray-like object...
137+
return {
138+
'dtype': x.dtype,
139+
'data': x.data,
140+
'shape': sh,
141+
'strides': sx,
142+
'offset': xmmv[ ind ], // the index of the first indexed element
143+
'order': x.order
144+
};
145+
}
146+
}
147+
148+
/**
149+
* Returns a function which copies an input ndarray to a contiguous ndarray workspace.
150+
*
151+
* @private
152+
* @param {NonNegativeInteger} len - number of elements in an ndarray
153+
* @param {ndarrayLike} workspace - ndarray workspace
154+
* @returns {Function} function which copies an input ndarray to a contiguous ndarray workspace
155+
*/
156+
function copy( len, workspace ) {
157+
// NOTE: the following properties must be set in the exact same order as in the input ndarray-like object in order to ensure that the returned object has the same hidden shape...
158+
var view = {
159+
'dtype': workspace.dtype,
160+
'data': workspace.data,
161+
'shape': [ len ],
162+
'strides': [ 1 ],
163+
'offset': workspace.offset,
164+
'order': workspace.order
165+
};
166+
return reshape;
167+
168+
/**
169+
* Copies an input ndarray to a contiguous ndarray workspace and returns a one-dimensional workspace view.
170+
*
171+
* @private
172+
* @param {ndarrayLike} x - input ndarray
173+
* @returns {ndarrayLike} one-dimensional workspace view
174+
*/
175+
function reshape( x ) {
176+
assign( [ x, workspace ] );
177+
return view;
178+
}
179+
}
180+
181+
182+
// MAIN //
183+
184+
/**
185+
* Returns a function for reshaping input ndarrays which have the same data type, shape, and strides as a provided ndarray.
186+
*
187+
* @private
188+
* @param {ndarrayLike} x - input ndarray
189+
* @param {string} x.dtype - input ndarray data type
190+
* @param {Collection} x.data - input ndarray data buffer
191+
* @param {NonNegativeIntegerArray} x.shape - input ndarray shape
192+
* @param {IntegerArray} x.strides - input ndarray strides
193+
* @param {NonNegativeInteger} x.offset - input ndarray index offset
194+
* @param {string} x.order - input ndarray memory layout
195+
* @returns {Function} function implementing a reshape strategy
196+
*/
197+
function strategy( x ) {
198+
var ndims;
199+
var xmmv;
200+
var len;
201+
var iox;
202+
var sh;
203+
var ns;
204+
var i;
205+
206+
// Resolve the number of array dimensions:
207+
sh = x.shape;
208+
ndims = sh.length;
209+
210+
// Check whether the ndarray is zero-dimensional...
211+
if ( ndims === 0 ) {
212+
return broadcast;
213+
}
214+
// Check whether the ndarray is already one-dimensional...
215+
if ( ndims === 1 ) {
216+
return identity;
217+
}
218+
// Determine the number of singleton dimensions...
219+
len = 1; // number of elements
220+
ns = 0; // number of singleton dimensions
221+
for ( i = 0; i < ndims; i++ ) {
222+
// Check whether the current dimension is a singleton dimension...
223+
if ( sh[ i ] === 1 ) {
224+
ns += 1;
225+
}
226+
len *= sh[ i ];
227+
}
228+
// Determine whether the ndarray has only **one** non-singleton dimension (e.g., ndims=4, shape=[10,1,1,1]) so that we can simply create an ndarray view without the singleton dimensions...
229+
if ( ns === ndims-1 ) {
230+
// Get the index of the non-singleton dimension...
231+
for ( i = 0; i < ndims; i++ ) {
232+
if ( sh[ i ] !== 1 ) {
233+
break;
234+
}
235+
}
236+
return squeeze( x, i );
237+
}
238+
iox = iterationOrder( x.strides ); // +/-1
239+
240+
// Determine whether we can avoid copying data...
241+
if ( iox !== 0 ) {
242+
// Determine the minimum and maximum linear indices which are accessible by the ndarray view:
243+
xmmv = minmaxViewBufferIndex( sh, x.strides, x.offset, [ 0, 0 ] );
244+
245+
// Determine whether we can ignore shape (and strides) and create a new one-dimensional ndarray view...
246+
if ( len === ( xmmv[1]-xmmv[0]+1 ) ) {
247+
return contiguous( len, iox );
248+
}
249+
// The ndarray is non-contiguous, so we cannot directly interpret as a one-dimensional ndarray...
250+
251+
// Fall-through to copying to a workspace ndarray...
252+
}
253+
// At this point, we're dealing with a non-contiguous multi-dimensional ndarray, so we need to copy to a contiguous workspace:
254+
return copy( len, ndarraylike2object( emptyLike( ndarraylike2ndarray( x ) ) ) ); // eslint-disable-line max-len
255+
}
256+
257+
258+
// EXPORTS //
259+
260+
module.exports = strategy;

0 commit comments

Comments
 (0)