11using System ;
22using NumSharp . Generic ;
3- using NumSharp . Utilities ;
43using System . Collections . Generic ;
5- using System . Diagnostics ;
6- using System . Threading ;
7- using System . Linq ;
8- using System . Threading . Tasks ;
9- using NumSharp . Backends ;
104using NumSharp . Backends . Kernels ;
115
126namespace NumSharp . Backends
@@ -16,48 +10,46 @@ public partial class DefaultEngine
1610 /// <summary>
1711 /// Return the indices of non-zero elements.
1812 /// </summary>
19- /// <param name="nd"></param>
20- /// <returns></returns>
13+ /// <remarks>
14+ /// NumPy-aligned behavior:
15+ /// - Returns tuple of arrays, one per dimension
16+ /// - For empty arrays, returns empty arrays with correct dtype (int)
17+ /// - Iterates in C-order (row-major)
18+ /// - Handles contiguous and strided arrays efficiently
19+ /// </remarks>
20+ /// <param name="nd">Input array</param>
21+ /// <returns>Array of NDArray<int>, one per dimension containing indices of non-zero elements</returns>
2122 public override NDArray < int > [ ] NonZero ( in NDArray nd )
2223 {
23- #if _REGEN
24- #region Compute
25- switch ( nd . typecode )
26- {
27- % foreach supported_dtypes, supported_dtypes_lowercase%
28- case NPTypeCode . #1 : return nonzeros < #2 > ( nd . MakeGeneric < #2 > ( ) ) ;
29- %
30- default :
31- throw new NotSupportedException ( ) ;
32- }
33- #endregion
34- #else
35-
36- #region Compute
37- switch ( nd . typecode )
38- {
39- case NPTypeCode . Boolean : return nonzeros < bool > ( nd . MakeGeneric < bool > ( ) ) ;
40- case NPTypeCode . Byte : return nonzeros < byte > ( nd . MakeGeneric < byte > ( ) ) ;
41- case NPTypeCode . Int16 : return nonzeros < short > ( nd . MakeGeneric < short > ( ) ) ;
42- case NPTypeCode . UInt16 : return nonzeros < ushort > ( nd . MakeGeneric < ushort > ( ) ) ;
43- case NPTypeCode . Int32 : return nonzeros < int > ( nd . MakeGeneric < int > ( ) ) ;
44- case NPTypeCode . UInt32 : return nonzeros < uint > ( nd . MakeGeneric < uint > ( ) ) ;
45- case NPTypeCode . Int64 : return nonzeros < long > ( nd . MakeGeneric < long > ( ) ) ;
46- case NPTypeCode . UInt64 : return nonzeros < ulong > ( nd . MakeGeneric < ulong > ( ) ) ;
47- case NPTypeCode . Char : return nonzeros < char > ( nd . MakeGeneric < char > ( ) ) ;
48- case NPTypeCode . Double : return nonzeros < double > ( nd . MakeGeneric < double > ( ) ) ;
49- case NPTypeCode . Single : return nonzeros < float > ( nd . MakeGeneric < float > ( ) ) ;
50- case NPTypeCode . Decimal : return nonzeros < decimal > ( nd . MakeGeneric < decimal > ( ) ) ;
51- default :
52- throw new NotSupportedException ( ) ;
53- }
54- #endregion
55- #endif
24+ // Type dispatch to generic implementation
25+ switch ( nd . typecode )
26+ {
27+ case NPTypeCode . Boolean : return nonzeros < bool > ( nd . MakeGeneric < bool > ( ) ) ;
28+ case NPTypeCode . Byte : return nonzeros < byte > ( nd . MakeGeneric < byte > ( ) ) ;
29+ case NPTypeCode . Int16 : return nonzeros < short > ( nd . MakeGeneric < short > ( ) ) ;
30+ case NPTypeCode . UInt16 : return nonzeros < ushort > ( nd . MakeGeneric < ushort > ( ) ) ;
31+ case NPTypeCode . Int32 : return nonzeros < int > ( nd . MakeGeneric < int > ( ) ) ;
32+ case NPTypeCode . UInt32 : return nonzeros < uint > ( nd . MakeGeneric < uint > ( ) ) ;
33+ case NPTypeCode . Int64 : return nonzeros < long > ( nd . MakeGeneric < long > ( ) ) ;
34+ case NPTypeCode . UInt64 : return nonzeros < ulong > ( nd . MakeGeneric < ulong > ( ) ) ;
35+ case NPTypeCode . Char : return nonzeros < char > ( nd . MakeGeneric < char > ( ) ) ;
36+ case NPTypeCode . Double : return nonzeros < double > ( nd . MakeGeneric < double > ( ) ) ;
37+ case NPTypeCode . Single : return nonzeros < float > ( nd . MakeGeneric < float > ( ) ) ;
38+ case NPTypeCode . Decimal : return nonzeros < decimal > ( nd . MakeGeneric < decimal > ( ) ) ;
39+ default :
40+ throw new NotSupportedException ( $ "NonZero not supported for type { nd . typecode } ") ;
41+ }
5642 }
5743
44+ /// <summary>
45+ /// Generic implementation of nonzero using ILKernelGenerator.
46+ /// Both contiguous and strided paths now use the unified IL-based approach.
47+ /// </summary>
5848 private static unsafe NDArray < int > [ ] nonzeros < T > ( NDArray < T > x ) where T : unmanaged
5949 {
50+ // Ensure at least 1D (NumPy behavior)
6051 x = np . atleast_1d ( x ) . MakeGeneric < T > ( ) ;
52+ var shape = x . Shape ;
6153 var size = x . size ;
6254 var ndim = x . ndim ;
6355
@@ -71,240 +63,19 @@ private static unsafe NDArray<int>[] nonzeros<T>(NDArray<T> x) where T : unmanag
7163 return emptyResult ;
7264 }
7365
74- // SIMD fast path for contiguous arrays
7566 var kp = DefaultKernelProvider ;
76- if ( x . Shape . IsContiguous && kp . Enabled && kp . VectorBits > 0 )
67+
68+ // SIMD fast path for contiguous arrays
69+ if ( shape . IsContiguous && kp . Enabled && kp . VectorBits > 0 )
7770 {
7871 var flatIndices = new List < int > ( Math . Max ( 16 , size / 4 ) ) ;
7972 kp . FindNonZero ( ( T * ) x . Address , size , flatIndices ) ;
8073 return kp . ConvertFlatToCoordinates ( flatIndices , x . shape ) ;
8174 }
8275
83- // Original path for non-contiguous arrays
84- var nonzeroCoords = new List < int [ ] > ( size / 3 ) ;
85- #if _REGEN
86- #region Compute
87- Func < int [ ] , int > getOffset = x . Shape . GetOffset ;
88- switch ( x . typecode ) {
89- % foreach supported_dtypes, supported_dtypes_lowercase %
90- case NPTypeCode. #1 : {
91- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
92- var coords = incr. Index;
93- var src = ( #2 * ) x . Address ;
94- int offset;
95- do
96- {
97- offset = getOffset ( coords ) ;
98- if ( ! ( src [ offset ] == default ( #2 ) ) )
99- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
100- } while ( incr . Next ( ) != null ) ;
101-
102- break ;
103- }
104- %
105- default : throw new NotSupportedException ( ) ;
106- }
107- #endregion
108- #else
109- #region Compute
110- Func < int [ ] , int > getOffset = x. Shape. GetOffset;
111- switch ( x . typecode ) {
112- case NPTypeCode . Boolean : {
113- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
114- var coords = incr. Index;
115- var src = ( bool * ) x . Address ;
116- int offset ;
117- do
118- {
119- offset = getOffset ( coords ) ;
120- if ( ! ( src [ offset ] == default ( bool ) ) )
121- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
122- } while ( incr . Next ( ) != null ) ;
123-
124- break ;
125- }
126- case NPTypeCode . Byte : {
127- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
128- var coords = incr. Index;
129- var src = ( byte * ) x . Address ;
130- int offset ;
131- do
132- {
133- offset = getOffset ( coords ) ;
134- if ( ! ( src [ offset ] == default ( byte ) ) )
135- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
136- } while ( incr . Next ( ) != null ) ;
137-
138- break ;
139- }
140- case NPTypeCode. Int16 : {
141- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
142- var coords = incr . Index ;
143- var src = ( short * ) x . Address ;
144- int offset;
145- do
146- {
147- offset = getOffset ( coords ) ;
148- if ( ! ( src [ offset ] == default ( short ) ) )
149- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
150- } while ( incr . Next ( ) != null ) ;
151-
152- break ;
153- }
154- case NPTypeCode. UInt16 : {
155- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
156- var coords = incr . Index ;
157- var src = ( ushort * ) x . Address ;
158- int offset;
159- do
160- {
161- offset = getOffset ( coords ) ;
162- if ( ! ( src [ offset ] == default ( ushort ) ) )
163- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
164- } while ( incr . Next ( ) != null ) ;
165-
166- break ;
167- }
168- case NPTypeCode. Int32 : {
169- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
170- var coords = incr . Index ;
171- var src = ( int * ) x . Address ;
172- int offset;
173- do
174- {
175- offset = getOffset ( coords ) ;
176- if ( ! ( src [ offset ] == default ( int ) ) )
177- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
178- } while ( incr . Next ( ) != null ) ;
179-
180- break ;
181- }
182- case NPTypeCode. UInt32 : {
183- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
184- var coords = incr . Index ;
185- var src = ( uint * ) x . Address ;
186- int offset;
187- do
188- {
189- offset = getOffset ( coords ) ;
190- if ( ! ( src [ offset ] == default ( uint ) ) )
191- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
192- } while ( incr . Next ( ) != null ) ;
193-
194- break ;
195- }
196- case NPTypeCode. Int64 : {
197- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
198- var coords = incr . Index ;
199- var src = ( long * ) x . Address ;
200- int offset;
201- do
202- {
203- offset = getOffset ( coords ) ;
204- if ( ! ( src [ offset ] == default ( long ) ) )
205- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
206- } while ( incr . Next ( ) != null ) ;
207-
208- break ;
209- }
210- case NPTypeCode. UInt64 : {
211- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
212- var coords = incr. Index;
213- var src = ( ulong * ) x . Address ;
214- int offset ;
215- do
216- {
217- offset = getOffset ( coords ) ;
218- if ( ! ( src [ offset ] == default ( ulong ) ) )
219- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
220- } while ( incr . Next ( ) != null ) ;
221-
222- break ;
223- }
224- case NPTypeCode . Char : {
225- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
226- var coords = incr. Index;
227- var src = ( char * ) x . Address ;
228- int offset ;
229- do
230- {
231- offset = getOffset ( coords ) ;
232- if ( ! ( src [ offset ] == default ( char ) ) )
233- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
234- } while ( incr . Next ( ) != null ) ;
235-
236- break ;
237- }
238- case NPTypeCode . Double : {
239- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
240- var coords = incr. Index;
241- var src = ( double * ) x . Address ;
242- int offset ;
243- do
244- {
245- offset = getOffset ( coords ) ;
246- if ( ! ( src [ offset ] == default ( double ) ) )
247- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
248- } while ( incr . Next ( ) != null ) ;
249-
250- break ;
251- }
252- case NPTypeCode . Single : {
253- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
254- var coords = incr. Index;
255- var src = ( float * ) x . Address ;
256- int offset ;
257- do
258- {
259- offset = getOffset ( coords ) ;
260- if ( ! ( src [ offset ] == default ( float ) ) )
261- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
262- } while ( incr . Next ( ) != null ) ;
263-
264- break ;
265- }
266- case NPTypeCode . Decimal : {
267- var incr = new ValueCoordinatesIncrementor ( x . shape ) ;
268- var coords = incr. Index;
269- var src = ( decimal * ) x . Address ;
270- int offset ;
271- do
272- {
273- offset = getOffset ( coords ) ;
274- if ( ! ( src [ offset ] == default ( decimal ) ) )
275- nonzeroCoords. Add ( coords . CloneArray ( ) ) ;
276- } while ( incr . Next ( ) != null ) ;
277-
278- break ;
279- }
280- default : throw new NotSupportedException ( ) ;
281- }
282- #endregion
283- #endif
284-
285- var len = nonzeroCoords. Count;
286- //create ndarray for each dimension
287- var ret = new NDArray < int > [ ndim ] ;
288- for ( int i = 0 ; i < x . ndim ; i++ )
289- ret[ i] = new NDArray< int > ( len) ;
290-
291- //create address for each dimension
292- var addresses = new int * [ ndim] ;
293- for ( int i = 0 ; i < ndim; i++ )
294- addresses[ i] = ( int * ) ret[ i] . Address;
295-
296- //extract coordinates
297- for ( int i = 0 ; i < len; i++ )
298- {
299- var coords = nonzeroCoords[ i] ;
300- for ( int axis = 0 ; axis < ndim; axis++ )
301- {
302- addresses[ axis] [ i] = coords[ axis] ;
303- }
304- } ;
305-
306- return ret;
76+ // Strided path for non-contiguous arrays (transposed, sliced, etc.)
77+ // Uses coordinate-based iteration via ILKernelGenerator
78+ return kp . FindNonZeroStrided ( ( T * ) x . Address , shape . dimensions , shape . strides , shape . offset ) ;
30779 }
308-
30980 }
31081}
0 commit comments