Repository navigation
Expand file tree
/
Copy pathDirectILKernelGenerator.cs
More file actions
2315 lines (2150 loc) · 136 KB
/
Copy pathDirectILKernelGenerator.cs
File metadata and controls
2315 lines (2150 loc) · 136 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using NumSharp.Utilities;
// =============================================================================
// DirectILKernelGenerator - IL-based SIMD kernel generation using DynamicMethod
// =============================================================================
//
// ARCHITECTURE OVERVIEW
// ---------------------
// This class generates high-performance kernels at runtime using IL emission.
// The JIT compiler can then optimize these kernels with full SIMD support (V128/V256/V512).
// Kernels are cached by operation key to avoid repeated IL generation.
//
// FLOW: Caller (DefaultEngine, np.*, NDArray ops)
// -> Requests kernel via Get*Kernel() or *Helper() methods
// -> DirectILKernelGenerator checks cache, generates IL if needed
// -> Returns delegate that caller invokes with array pointers
//
// DESIGN: Static class - all kernel methods are static.
// Call them directly from DefaultEngine.
//
// EXCEPTION HANDLING
// ------------------
// All TryGet*Kernel() methods use catch-all exception handling that returns null.
// This is intentional graceful degradation: if IL generation fails for any reason
// (unsupported type, reflection error, invalid IL sequence), the caller receives
// null and falls back to an alternative code path (typically scalar loops or
// throwing NotSupportedException with a descriptive message).
//
// This pattern exists in 14 locations across the partial class files:
// - Binary.cs: TryGenerateContiguousKernel
// - MixedType.cs: TryGetMixedTypeKernel
// - MatMul.cs: GenerateMatMulKernelIL
// - Unary.cs: TryGetUnaryKernel
// - Shift.cs: GetShiftScalarKernel, GetShiftArrayKernel
// - Scan.cs: TryGetCumulativeKernel, TryGetCumulativeAxisKernel
// - Reduction.cs: TryGetTypedElementReductionKernel
// - Comparison.cs: TryGetComparisonKernel
// - DirectILKernelGenerator.cs: Core kernel infrastructure
//
// =============================================================================
// PARTIAL CLASS FILES
// =============================================================================
//
// DirectILKernelGenerator.cs (THIS FILE)
// OWNERSHIP: Core infrastructure - foundation for all other partial files
// RESPONSIBILITY:
// - Static kernel generation methods used by DefaultEngine
// - Global state: Enabled flag, VectorBits/VectorBytes (detected at startup)
// - Type mapping: NPTypeCode <-> CLR Type <-> Vector type conversions
// - Shared IL emission primitives used by all other partials
// DEPENDENCIES: None (other partials depend on this)
// KEY MEMBERS:
// - Enabled, VectorBits, VectorBytes - runtime SIMD capability
// - GetVectorContainerType(), GetVectorType() - V128/V256/V512 type selection
// - GetTypeSize(), GetClrType(), CanUseSimd(), IsUnsigned() - type utilities
// - EmitLoadIndirect(), EmitStoreIndirect() - memory access IL
// - EmitConvertTo(), EmitScalarOperation() - type conversion and scalar ops
// - EmitVectorLoad/Store/Create/Operation() - SIMD operations
//
// DirectILKernelGenerator.Binary.cs
// OWNERSHIP: Same-type binary operations on contiguous arrays (fast path)
// RESPONSIBILITY:
// - Optimized kernels when both operands have identical type and layout
// - SIMD loop + scalar tail for Add, Sub, Mul, Div
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by DefaultEngine for same-type contiguous operations
// KEY MEMBERS:
// - ContiguousKernel<T> delegate, _contiguousKernelCache
// - GetContiguousKernel<T>()
// - Generic helpers: IsSimdSupported<T>(), EmitLoadIndirect<T>(), etc.
//
// DirectILKernelGenerator.MixedType.cs
// OWNERSHIP: Mixed-type binary operations with type promotion
// RESPONSIBILITY:
// - Handles all binary ops where operand types may differ
// - Generates path-specific kernels based on stride patterns
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by DefaultEngine for general binary operations
// KEY MEMBERS:
// - MixedTypeKernel delegate, _mixedTypeCache
// - GetMixedTypeKernel(), TryGetMixedTypeKernel()
// - Path generators: GenerateSimdFullKernel(), GenerateGeneralKernel(), etc.
// - Loop emitters: EmitScalarFullLoop(), EmitSimdFullLoop(), EmitGeneralLoop()
//
// DirectILKernelGenerator.Unary.cs
// OWNERSHIP: Unary element-wise operations
// RESPONSIBILITY:
// - Math functions: Negate, Abs, Sqrt, Sin, Cos, Exp, Log, Sign, Floor, Ceil, etc.
// - Scalar delegate generation for single-value operations (Func<TIn,TOut>)
// - Binary scalar delegates for mixed-type scalar operations
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by DefaultEngine for unary ops; scalar delegates used in broadcasting
// KEY MEMBERS:
// - UnaryKernel delegate, _unaryCache, _unaryScalarCache, _binaryScalarCache
// - GetUnaryKernel(), GetUnaryScalarDelegate(), GetBinaryScalarDelegate()
// - EmitUnaryScalarOperation(), EmitMathCall(), EmitSignCall()
//
// DirectILKernelGenerator.Comparison.cs
// OWNERSHIP: Comparison operations returning boolean arrays
// RESPONSIBILITY:
// - Element-wise comparisons: ==, !=, <, >, <=, >=
// - SIMD comparison with efficient mask-to-bool extraction
// - Scalar comparison delegates for single-value operations
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by NDArray comparison operators (==, !=, <, >, etc.)
// KEY MEMBERS:
// - ComparisonKernel delegate, _comparisonCache, _comparisonScalarCache
// - GetComparisonKernel(), GetComparisonScalarDelegate()
// - EmitVectorComparison(), EmitMaskToBoolExtraction()
//
// DirectILKernelGenerator.Reduction.cs
// OWNERSHIP: Reduction operations and specialized SIMD helpers
// RESPONSIBILITY:
// - Reductions: Sum, Prod, Min, Max, Mean, ArgMax, ArgMin, All, Any
// - SIMD helpers called DIRECTLY by other NumSharp code (not just via kernels):
// * All/Any with early-exit optimization
// * ArgMax/ArgMin with SIMD two-pass (find value, then find index)
// * Boolean masking: CountTrue, CopyMaskedElements
// (np.nonzero / np.argwhere live in DirectILKernelGenerator.Argwhere.cs +
// DirectILKernelGenerator.NonZero.cs as per-dtype IL kernels.)
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Kernels called by DefaultEngine; helpers called directly by np.all/any/nonzero/masking
// KEY MEMBERS:
// - TypedElementReductionKernel<T> delegate, _elementReductionCache
// - GetTypedElementReductionKernel<T>()
// - AllSimdHelper<T>(), AnySimdHelper<T>() - early-exit boolean reductions
// - ArgMaxSimdHelper<T>(), ArgMinSimdHelper<T>() - index-tracking reductions
// - CountTrueSimdHelper(), CopyMaskedElementsHelper<T>()
// - EmitTreeReduction(), EmitVectorHorizontalReduction()
//
// =============================================================================
// =============================================================================
// DirectILKernelGenerator - IL-based SIMD kernel generation using DynamicMethod
// =============================================================================
//
// ARCHITECTURE OVERVIEW
// ---------------------
// This partial class generates high-performance kernels at runtime using IL emission.
// The JIT compiler can then optimize these kernels with full SIMD support (V128/V256/V512).
// Kernels are cached by operation key to avoid repeated IL generation.
//
// FLOW: Caller (DefaultEngine, np.*, NDArray ops)
// -> Requests kernel via Get*Kernel() or *Helper() methods
// -> DirectILKernelGenerator checks cache, generates IL if needed
// -> Returns delegate that caller invokes with array pointers
//
// =============================================================================
// PARTIAL CLASS FILES
// =============================================================================
//
// DirectILKernelGenerator.cs (THIS FILE)
// OWNERSHIP: Core infrastructure - foundation for all other partial files
// RESPONSIBILITY:
// - Global state: Enabled flag, VectorBits/VectorBytes (detected at startup)
// - Type mapping: NPTypeCode <-> CLR Type <-> Vector type conversions
// - Shared IL emission primitives used by all other partials
// DEPENDENCIES: None (other partials depend on this)
// KEY MEMBERS:
// - Enabled, VectorBits, VectorBytes - runtime SIMD capability
// - GetVectorContainerType(), GetVectorType() - V128/V256/V512 type selection
// - GetTypeSize(), GetClrType(), CanUseSimd(), IsUnsigned() - type utilities
// - EmitLoadIndirect(), EmitStoreIndirect() - memory access IL
// - EmitConvertTo(), EmitScalarOperation() - type conversion and scalar ops
// - EmitVectorLoad/Store/Create/Operation() - SIMD operations
//
// DirectILKernelGenerator.Binary.cs
// OWNERSHIP: Same-type binary operations on contiguous arrays (fast path)
// RESPONSIBILITY:
// - Optimized kernels when both operands have identical type and layout
// - SIMD loop + scalar tail for Add, Sub, Mul, Div
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by DefaultEngine for same-type contiguous operations
// KEY MEMBERS:
// - ContiguousKernel<T> delegate, _contiguousKernelCache
// - GetContiguousKernel<T>(), GenerateUnifiedKernel<T>()
// - Generic helpers: IsSimdSupported<T>(), EmitLoadIndirect<T>(), etc.
//
// DirectILKernelGenerator.MixedType.cs
// OWNERSHIP: Mixed-type binary operations with type promotion
// RESPONSIBILITY:
// - Handles all binary ops where operand types may differ
// - Generates path-specific kernels based on stride patterns
// - Owns ClearAll() which clears ALL caches across all partials
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by DefaultEngine for general binary operations
// KEY MEMBERS:
// - MixedTypeKernel delegate, _mixedTypeCache
// - GetMixedTypeKernel(), TryGetMixedTypeKernel(), ClearAll()
// - Path generators: GenerateSimdFullKernel(), GenerateGeneralKernel(), etc.
// - Loop emitters: EmitScalarFullLoop(), EmitSimdFullLoop(), EmitGeneralLoop()
//
// DirectILKernelGenerator.Unary.cs
// OWNERSHIP: Unary element-wise operations
// RESPONSIBILITY:
// - Math functions: Negate, Abs, Sqrt, Sin, Cos, Exp, Log, Sign, Floor, Ceil, etc.
// - Scalar delegate generation for single-value operations (Func<TIn,TOut>)
// - Binary scalar delegates for mixed-type scalar operations
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by DefaultEngine for unary ops; scalar delegates used in broadcasting
// KEY MEMBERS:
// - UnaryKernel delegate, _unaryCache, _unaryScalarCache, _binaryScalarCache
// - GetUnaryKernel(), GetUnaryScalarDelegate(), GetBinaryScalarDelegate()
// - EmitUnaryScalarOperation(), EmitMathCall(), EmitSignCall()
//
// DirectILKernelGenerator.Comparison.cs
// OWNERSHIP: Comparison operations returning boolean arrays
// RESPONSIBILITY:
// - Element-wise comparisons: ==, !=, <, >, <=, >=
// - SIMD comparison with efficient mask-to-bool extraction
// - Scalar comparison delegates for single-value operations
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Called by NDArray comparison operators (==, !=, <, >, etc.)
// KEY MEMBERS:
// - ComparisonKernel delegate, _comparisonCache, _comparisonScalarCache
// - GetComparisonKernel(), GetComparisonScalarDelegate()
// - EmitVectorComparison(), EmitMaskToBoolExtraction()
//
// DirectILKernelGenerator.Reduction.cs
// OWNERSHIP: Reduction operations and specialized SIMD helpers
// RESPONSIBILITY:
// - Reductions: Sum, Prod, Min, Max, Mean, ArgMax, ArgMin, All, Any
// - SIMD helpers called DIRECTLY by other NumSharp code (not just via kernels):
// * All/Any with early-exit optimization
// * ArgMax/ArgMin with SIMD two-pass (find value, then find index)
// * Boolean masking: CountTrue, CopyMaskedElements
// (np.nonzero / np.argwhere live in DirectILKernelGenerator.Argwhere.cs +
// DirectILKernelGenerator.NonZero.cs as per-dtype IL kernels.)
// DEPENDENCIES: Uses core emit helpers from DirectILKernelGenerator.cs
// FLOW: Kernels called by DefaultEngine; helpers called directly by np.all/any/nonzero/masking
// KEY MEMBERS:
// - TypedElementReductionKernel<T> delegate, _elementReductionCache
// - GetTypedElementReductionKernel<T>(), ClearReduction()
// - AllSimdHelper<T>(), AnySimdHelper<T>() - early-exit boolean reductions
// - ArgMaxSimdHelper<T>(), ArgMinSimdHelper<T>() - index-tracking reductions
// - CountTrueSimdHelper(), CopyMaskedElementsHelper<T>()
// - EmitTreeReduction(), EmitVectorHorizontalReduction()
//
// =============================================================================
namespace NumSharp.Backends.Kernels
{
/// <summary>
/// Generates IL-based SIMD kernels using DynamicMethod.
/// These kernels provide ~10-15% speedup over the C# reference implementations
/// by allowing the JIT to inline Vector256 operations more aggressively.
///
/// This class is internal to NumSharp.Backends - all kernel access should go
/// through TensorEngine/DefaultEngine, not directly from np.* or NDArray.
/// </summary>
public static partial class DirectILKernelGenerator
{
#region Static Configuration
/// <summary>
/// Provider name for diagnostics.
/// </summary>
public static string Name => "IL";
/// <summary>
/// Whether IL generation is enabled. Can be disabled for debugging.
/// </summary>
public static bool Enabled { get; set; } = true;
/// <summary>
/// Detected vector width at startup: 512, 256, 128, or 0 (no SIMD).
/// </summary>
public static readonly int VectorBits =
Vector512.IsHardwareAccelerated ? 512 :
Vector256.IsHardwareAccelerated ? 256 :
Vector128.IsHardwareAccelerated ? 128 : 0;
/// <summary>
/// Number of bytes per vector register.
/// </summary>
public static readonly int VectorBytes = VectorBits / 8;
#endregion
#region Cached MethodInfo Lookups
/// <summary>
/// Pre-cached MethodInfo references for frequently used reflection calls.
/// Caching these avoids repeated GetMethod() lookups during kernel generation.
/// All fields use ?? throw to fail fast at type load if a method is not found.
/// </summary>
private static partial class CachedMethods
{
// Math methods (double versions)
public static readonly MethodInfo MathPow = typeof(Math).GetMethod(nameof(Math.Pow), new[] { typeof(double), typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, nameof(Math.Pow));
public static readonly MethodInfo MathFPow = typeof(MathF).GetMethod(nameof(MathF.Pow), new[] { typeof(float), typeof(float) })
?? throw new MissingMethodException(typeof(MathF).FullName, nameof(MathF.Pow));
public static readonly MethodInfo MathFloor = typeof(Math).GetMethod(nameof(Math.Floor), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, nameof(Math.Floor));
public static readonly MethodInfo MathAtan2 = typeof(Math).GetMethod(nameof(Math.Atan2), new[] { typeof(double), typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, nameof(Math.Atan2));
// np.logaddexp / np.logaddexp2 / np.nextafter / np.copysign scalar kernels — one static
// helper per (op, loop dtype), resolved through GetLogAddNextMethod. Operands reach
// EmitScalarOperation already in the loop dtype, so the kernel is a single Call to the
// matching signature (double/float/Half/decimal). Same shape as the NDDiv helpers below.
private static MethodInfo LA(string name, Type t) =>
typeof(Utilities.NDLogAddExpMath).GetMethod(name, new[] { t, t })
?? throw new MissingMethodException(typeof(Utilities.NDLogAddExpMath).FullName, name);
public static readonly MethodInfo LogAddExpD = LA(nameof(Utilities.NDLogAddExpMath.LogAddExp), typeof(double));
public static readonly MethodInfo LogAddExpF = LA(nameof(Utilities.NDLogAddExpMath.LogAddExpF), typeof(float));
public static readonly MethodInfo LogAddExpH = LA(nameof(Utilities.NDLogAddExpMath.LogAddExpHalf), typeof(Half));
public static readonly MethodInfo LogAddExpDec = LA(nameof(Utilities.NDLogAddExpMath.LogAddExpDecimal), typeof(decimal));
public static readonly MethodInfo LogAddExp2D = LA(nameof(Utilities.NDLogAddExpMath.LogAddExp2), typeof(double));
public static readonly MethodInfo LogAddExp2F = LA(nameof(Utilities.NDLogAddExpMath.LogAddExp2F), typeof(float));
public static readonly MethodInfo LogAddExp2H = LA(nameof(Utilities.NDLogAddExpMath.LogAddExp2Half), typeof(Half));
public static readonly MethodInfo LogAddExp2Dec = LA(nameof(Utilities.NDLogAddExpMath.LogAddExp2Decimal), typeof(decimal));
public static readonly MethodInfo NextAfterD = LA(nameof(Utilities.NDLogAddExpMath.NextAfter), typeof(double));
public static readonly MethodInfo NextAfterF = LA(nameof(Utilities.NDLogAddExpMath.NextAfterF), typeof(float));
public static readonly MethodInfo NextAfterH = LA(nameof(Utilities.NDLogAddExpMath.NextAfterHalf), typeof(Half));
public static readonly MethodInfo NextAfterDec = LA(nameof(Utilities.NDLogAddExpMath.NextAfterDecimal), typeof(decimal));
public static readonly MethodInfo CopySignD = LA(nameof(Utilities.NDLogAddExpMath.CopySign), typeof(double));
public static readonly MethodInfo CopySignF = LA(nameof(Utilities.NDLogAddExpMath.CopySignF), typeof(float));
public static readonly MethodInfo CopySignH = LA(nameof(Utilities.NDLogAddExpMath.CopySignHalf), typeof(Half));
public static readonly MethodInfo CopySignDec = LA(nameof(Utilities.NDLogAddExpMath.CopySignDecimal), typeof(decimal));
// Integer power helpers (squared-exponentiation with native wrapping).
// Used by EmitPowerOperation when result type is integer to preserve
// NumPy's exact-wrap semantics that Math.Pow's double round-trip loses.
public static readonly MethodInfo IntPowSByte = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowSByte), new[] { typeof(sbyte), typeof(sbyte) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowSByte));
public static readonly MethodInfo IntPowByte = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowByte), new[] { typeof(byte), typeof(byte) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowByte));
public static readonly MethodInfo IntPowInt16 = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowInt16), new[] { typeof(short), typeof(short) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowInt16));
public static readonly MethodInfo IntPowUInt16 = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowUInt16), new[] { typeof(ushort), typeof(ushort) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowUInt16));
public static readonly MethodInfo IntPowChar = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowChar), new[] { typeof(char), typeof(char) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowChar));
public static readonly MethodInfo IntPowInt32 = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowInt32), new[] { typeof(int), typeof(int) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowInt32));
public static readonly MethodInfo IntPowUInt32 = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowUInt32), new[] { typeof(uint), typeof(uint) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowUInt32));
public static readonly MethodInfo IntPowInt64 = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowInt64), new[] { typeof(long), typeof(long) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowInt64));
public static readonly MethodInfo IntPowUInt64 = typeof(Utilities.NDIntegerPower).GetMethod(
nameof(Utilities.NDIntegerPower.PowUInt64), new[] { typeof(ulong), typeof(ulong) })
?? throw new MissingMethodException(typeof(Utilities.NDIntegerPower).FullName, nameof(Utilities.NDIntegerPower.PowUInt64));
// floor-division / remainder helpers (NDDivision) — NumPy-exact divide-by-zero (-> 0
// for integers, ±inf/nan for floats) and floored-sign semantics. Used by
// EmitFloorDivideOperation / EmitModOperation in place of the old double round-trip.
private static MethodInfo NDDiv(string name, Type t) =>
typeof(Utilities.NDDivision).GetMethod(name, new[] { t, t })
?? throw new MissingMethodException(typeof(Utilities.NDDivision).FullName, name);
public static readonly MethodInfo FloorDivSByte = NDDiv(nameof(Utilities.NDDivision.FloorDivSByte), typeof(sbyte));
public static readonly MethodInfo FloorDivByte = NDDiv(nameof(Utilities.NDDivision.FloorDivByte), typeof(byte));
public static readonly MethodInfo FloorDivInt16 = NDDiv(nameof(Utilities.NDDivision.FloorDivInt16), typeof(short));
public static readonly MethodInfo FloorDivUInt16 = NDDiv(nameof(Utilities.NDDivision.FloorDivUInt16), typeof(ushort));
public static readonly MethodInfo FloorDivChar = NDDiv(nameof(Utilities.NDDivision.FloorDivChar), typeof(char));
public static readonly MethodInfo FloorDivInt32 = NDDiv(nameof(Utilities.NDDivision.FloorDivInt32), typeof(int));
public static readonly MethodInfo FloorDivUInt32 = NDDiv(nameof(Utilities.NDDivision.FloorDivUInt32), typeof(uint));
public static readonly MethodInfo FloorDivInt64 = NDDiv(nameof(Utilities.NDDivision.FloorDivInt64), typeof(long));
public static readonly MethodInfo FloorDivUInt64 = NDDiv(nameof(Utilities.NDDivision.FloorDivUInt64), typeof(ulong));
public static readonly MethodInfo FloorDivSingle = NDDiv(nameof(Utilities.NDDivision.FloorDivSingle), typeof(float));
public static readonly MethodInfo FloorDivDouble = NDDiv(nameof(Utilities.NDDivision.FloorDivDouble), typeof(double));
public static readonly MethodInfo RemSByte = NDDiv(nameof(Utilities.NDDivision.RemSByte), typeof(sbyte));
public static readonly MethodInfo RemByte = NDDiv(nameof(Utilities.NDDivision.RemByte), typeof(byte));
public static readonly MethodInfo RemInt16 = NDDiv(nameof(Utilities.NDDivision.RemInt16), typeof(short));
public static readonly MethodInfo RemUInt16 = NDDiv(nameof(Utilities.NDDivision.RemUInt16), typeof(ushort));
public static readonly MethodInfo RemChar = NDDiv(nameof(Utilities.NDDivision.RemChar), typeof(char));
public static readonly MethodInfo RemInt32 = NDDiv(nameof(Utilities.NDDivision.RemInt32), typeof(int));
public static readonly MethodInfo RemUInt32 = NDDiv(nameof(Utilities.NDDivision.RemUInt32), typeof(uint));
public static readonly MethodInfo RemInt64 = NDDiv(nameof(Utilities.NDDivision.RemInt64), typeof(long));
public static readonly MethodInfo RemUInt64 = NDDiv(nameof(Utilities.NDDivision.RemUInt64), typeof(ulong));
public static readonly MethodInfo RemSingle = NDDiv(nameof(Utilities.NDDivision.RemSingle), typeof(float));
public static readonly MethodInfo RemDouble = NDDiv(nameof(Utilities.NDDivision.RemDouble), typeof(double));
// Decimal conversion methods (to decimal)
public static readonly MethodInfo DecimalImplicitFromInt = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(int) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(int)");
public static readonly MethodInfo DecimalImplicitFromByte = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(byte) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(byte)");
public static readonly MethodInfo DecimalImplicitFromSByte = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(sbyte) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(sbyte)");
public static readonly MethodInfo DecimalImplicitFromShort = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(short) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(short)");
public static readonly MethodInfo DecimalImplicitFromUShort = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(ushort) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(ushort)");
public static readonly MethodInfo DecimalImplicitFromUInt = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(uint) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(uint)");
public static readonly MethodInfo DecimalImplicitFromLong = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(long) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(long)");
public static readonly MethodInfo DecimalImplicitFromULong = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(ulong) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Implicit(ulong)");
public static readonly MethodInfo DecimalExplicitFromFloat = typeof(decimal).GetMethod("op_Explicit", new[] { typeof(float) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Explicit(float)");
public static readonly MethodInfo DecimalExplicitFromDouble = typeof(decimal).GetMethod("op_Explicit", new[] { typeof(double) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Explicit(double)");
// Decimal conversion methods (from decimal)
public static readonly MethodInfo DecimalToByte = typeof(decimal).GetMethod("ToByte", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToByte");
public static readonly MethodInfo DecimalToSByte = typeof(decimal).GetMethod("ToSByte", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToSByte");
public static readonly MethodInfo DecimalToInt16 = typeof(decimal).GetMethod("ToInt16", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToInt16");
public static readonly MethodInfo DecimalToUInt16 = typeof(decimal).GetMethod("ToUInt16", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToUInt16");
public static readonly MethodInfo DecimalToInt32 = typeof(decimal).GetMethod("ToInt32", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToInt32");
public static readonly MethodInfo DecimalToUInt32 = typeof(decimal).GetMethod("ToUInt32", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToUInt32");
public static readonly MethodInfo DecimalToInt64 = typeof(decimal).GetMethod("ToInt64", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToInt64");
public static readonly MethodInfo DecimalToUInt64 = typeof(decimal).GetMethod("ToUInt64", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToUInt64");
public static readonly MethodInfo DecimalToSingle = typeof(decimal).GetMethod("ToSingle", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToSingle");
public static readonly MethodInfo DecimalToDouble = typeof(decimal).GetMethod("ToDouble", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "ToDouble");
// Decimal operator methods
public static readonly MethodInfo DecimalOpAddition = typeof(decimal).GetMethod("op_Addition",
BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(decimal), typeof(decimal) }, null)
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Addition");
public static readonly MethodInfo DecimalOpSubtraction = typeof(decimal).GetMethod("op_Subtraction",
BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(decimal), typeof(decimal) }, null)
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Subtraction");
public static readonly MethodInfo DecimalOpMultiply = typeof(decimal).GetMethod("op_Multiply",
BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(decimal), typeof(decimal) }, null)
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Multiply");
public static readonly MethodInfo DecimalOpDivision = typeof(decimal).GetMethod("op_Division",
BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(decimal), typeof(decimal) }, null)
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Division");
public static readonly MethodInfo DecimalFloor = typeof(decimal).GetMethod(nameof(decimal.Floor),
BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(decimal) }, null)
?? throw new MissingMethodException(typeof(decimal).FullName, nameof(decimal.Floor));
// NumSharp.Utilities.DecimalMath methods
public static readonly MethodInfo DecimalMathPow = typeof(Utilities.DecimalMath).GetMethod(
nameof(Utilities.DecimalMath.Pow), BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof(decimal), typeof(decimal) }, null)
?? throw new MissingMethodException(typeof(Utilities.DecimalMath).FullName, nameof(Utilities.DecimalMath.Pow));
public static readonly MethodInfo DecimalMathATan2 = typeof(Utilities.DecimalMath).GetMethod(
nameof(Utilities.DecimalMath.ATan2), BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof(decimal), typeof(decimal) }, null)
?? throw new MissingMethodException(typeof(Utilities.DecimalMath).FullName, nameof(Utilities.DecimalMath.ATan2));
// Decimal fields
public static readonly FieldInfo DecimalZero = typeof(decimal).GetField(nameof(decimal.Zero))
?? throw new MissingFieldException(typeof(decimal).FullName, nameof(decimal.Zero));
public static readonly FieldInfo DecimalOne = typeof(decimal).GetField(nameof(decimal.One))
?? throw new MissingFieldException(typeof(decimal).FullName, nameof(decimal.One));
public static readonly FieldInfo DecimalMinValue = typeof(decimal).GetField(nameof(decimal.MinValue))
?? throw new MissingFieldException(typeof(decimal).FullName, nameof(decimal.MinValue));
public static readonly FieldInfo DecimalMaxValue = typeof(decimal).GetField(nameof(decimal.MaxValue))
?? throw new MissingFieldException(typeof(decimal).FullName, nameof(decimal.MaxValue));
// Additional decimal operator methods
public static readonly MethodInfo DecimalOpUnaryNegation = typeof(decimal).GetMethod("op_UnaryNegation", new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_UnaryNegation");
public static readonly MethodInfo DecimalOpEquality = typeof(decimal).GetMethod("op_Equality", new[] { typeof(decimal), typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, "op_Equality");
public static readonly MethodInfo DecimalTruncate = typeof(decimal).GetMethod(nameof(decimal.Truncate), new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(decimal).FullName, nameof(decimal.Truncate));
// Math methods for decimal
public static readonly MethodInfo MathAbsDecimal = typeof(Math).GetMethod(nameof(Math.Abs), new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(Math).FullName, "Abs(decimal)");
public static readonly MethodInfo MathSignDecimal = typeof(Math).GetMethod(nameof(Math.Sign), new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(Math).FullName, "Sign(decimal)");
public static readonly MethodInfo MathCeilingDecimal = typeof(Math).GetMethod(nameof(Math.Ceiling), new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(Math).FullName, "Ceiling(decimal)");
public static readonly MethodInfo MathFloorDecimal = typeof(Math).GetMethod(nameof(Math.Floor), new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(Math).FullName, "Floor(decimal)");
public static readonly MethodInfo MathRoundDecimal = typeof(Math).GetMethod(nameof(Math.Round), new[] { typeof(decimal) })
?? throw new MissingMethodException(typeof(Math).FullName, "Round(decimal)");
// Math methods for double
public static readonly MethodInfo MathAbsDouble = typeof(Math).GetMethod(nameof(Math.Abs), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, "Abs(double)");
public static readonly MethodInfo MathExp = typeof(Math).GetMethod(nameof(Math.Exp), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, nameof(Math.Exp));
public static readonly MethodInfo MathLog = typeof(Math).GetMethod(nameof(Math.Log), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, nameof(Math.Log));
public static readonly MethodInfo MathCbrt = typeof(Math).GetMethod(nameof(Math.Cbrt), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, nameof(Math.Cbrt));
// MathF methods for float
public static readonly MethodInfo MathFAbsFloat = typeof(MathF).GetMethod(nameof(MathF.Abs), new[] { typeof(float) })
?? throw new MissingMethodException(typeof(MathF).FullName, nameof(MathF.Abs));
public static readonly MethodInfo MathFSign = typeof(MathF).GetMethod(nameof(MathF.Sign), new[] { typeof(float) })
?? throw new MissingMethodException(typeof(MathF).FullName, nameof(MathF.Sign));
// Math.Sign methods
public static readonly MethodInfo MathSignDouble = typeof(Math).GetMethod(nameof(Math.Sign), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, "Sign(double)");
// IsNaN / IsInfinity / IsFinite methods
public static readonly MethodInfo FloatIsNaN = typeof(float).GetMethod(nameof(float.IsNaN), new[] { typeof(float) })
?? throw new MissingMethodException(typeof(float).FullName, nameof(float.IsNaN));
public static readonly MethodInfo DoubleIsNaN = typeof(double).GetMethod(nameof(double.IsNaN), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(double).FullName, nameof(double.IsNaN));
public static readonly MethodInfo DoubleIsInfinity = typeof(double).GetMethod(nameof(double.IsInfinity), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(double).FullName, nameof(double.IsInfinity));
public static readonly MethodInfo DoubleIsFinite = typeof(double).GetMethod(nameof(double.IsFinite), new[] { typeof(double) })
?? throw new MissingMethodException(typeof(double).FullName, nameof(double.IsFinite));
public static readonly MethodInfo MathCopySign = typeof(Math).GetMethod(nameof(Math.CopySign), new[] { typeof(double), typeof(double) })
?? throw new MissingMethodException(typeof(Math).FullName, nameof(Math.CopySign));
// Unsafe methods
public static readonly MethodInfo UnsafeInitBlockUnaligned = typeof(Unsafe).GetMethod(nameof(Unsafe.InitBlockUnaligned),
new[] { typeof(void*), typeof(byte), typeof(uint) })
?? throw new MissingMethodException(typeof(Unsafe).FullName, nameof(Unsafe.InitBlockUnaligned));
// (Vector256 operator methods used by MatMul moved to VectorMethodCache.Operator.)
// Half conversion methods (Half is a struct with operator methods, not IConvertible)
public static readonly MethodInfo HalfToDouble = typeof(Half).GetMethods(BindingFlags.Public | BindingFlags.Static)
.First(m => m.Name == "op_Explicit" && m.ReturnType == typeof(double) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Half));
public static readonly MethodInfo DoubleToHalf = typeof(Half).GetMethods(BindingFlags.Public | BindingFlags.Static)
.First(m => m.Name == "op_Explicit" && m.ReturnType == typeof(Half) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(double));
// Half <-> float (F16C-lowered by the JIT: [Intrinsic] op_Explicit → vcvtph2ps / vcvtps2ph).
// These are the fast, NumPy-HALF-loop-matching conversions: NumPy's npy_half loops widen
// to float32 (npy_half_to_float), compute the CRT float function, and round back
// (npy_float_to_half) — see EmitUnaryHalfViaFloat. The double bridge above is 2 conversions
// deep (Half→float→double) and drags in the slower double math functions.
public static readonly MethodInfo HalfToFloat = typeof(Half).GetMethods(BindingFlags.Public | BindingFlags.Static)
.First(m => m.Name == "op_Explicit" && m.ReturnType == typeof(float) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Half));
public static readonly MethodInfo FloatToHalf = typeof(Half).GetMethods(BindingFlags.Public | BindingFlags.Static)
.First(m => m.Name == "op_Explicit" && m.ReturnType == typeof(Half) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(float));
// float.Exp2 == the CRT exp2f NumPy's HALF_exp2 loop calls (npy_exp2f). Used ONLY on the
// Half path: it is the correctly-rounded software 2^x, byte-identical to NumPy's half loop
// (finite AND NaN payload). The float32-array path uses NDFloatMath.Exp2 (the fast SIMD
// ≤1-ULP kernel) instead — but that kernel's scalar entry is far slower here than float.Exp2
// and blanks the NaN payload, so the half loop deliberately keeps the CRT.
public static readonly MethodInfo SingleExp2Crt = typeof(float).GetMethod("Exp2", BindingFlags.Public | BindingFlags.Static, new[] { typeof(float) })
?? throw new MissingMethodException(typeof(float).FullName, "Exp2(float)");
public static readonly MethodInfo HalfIsNaN = typeof(Half).GetMethod("IsNaN", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "IsNaN");
// Half static properties (NaN, Zero, PositiveInfinity, NegativeInfinity are properties, not fields)
public static readonly MethodInfo HalfNaN = typeof(Half).GetProperty("NaN", BindingFlags.Public | BindingFlags.Static)!.GetGetMethod()
?? throw new MissingMethodException(typeof(Half).FullName, "NaN");
public static readonly MethodInfo HalfZero = typeof(Half).GetProperty("Zero", BindingFlags.Public | BindingFlags.Static)!.GetGetMethod()
?? throw new MissingMethodException(typeof(Half).FullName, "Zero");
public static readonly MethodInfo HalfPositiveInfinity = typeof(Half).GetProperty("PositiveInfinity", BindingFlags.Public | BindingFlags.Static)!.GetGetMethod()
?? throw new MissingMethodException(typeof(Half).FullName, "PositiveInfinity");
public static readonly MethodInfo HalfNegativeInfinity = typeof(Half).GetProperty("NegativeInfinity", BindingFlags.Public | BindingFlags.Static)!.GetGetMethod()
?? throw new MissingMethodException(typeof(Half).FullName, "NegativeInfinity");
// Complex methods and fields (Complex uses static fields, not properties).
// ComplexAbs routes through NDComplexMath.Abs (npy_cabs / C99 hypot semantics) rather
// than Complex.Abs directly: the BCL's private Hypot returns NaN for abs(NaN+inf*i) on
// net8.0, where NumPy returns +inf. The helper defers to Complex.Abs for every
// finite/NaN-only input, so magnitudes that already match NumPy stay bit-identical.
public static readonly MethodInfo ComplexAbs = typeof(Utilities.NDComplexMath).GetMethod("Abs", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(Utilities.NDComplexMath).FullName, "Abs");
public static readonly MethodInfo ComplexDivisionByDouble = typeof(System.Numerics.Complex).GetMethod("op_Division", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex), typeof(double) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "op_Division(Complex, double)");
public static readonly FieldInfo ComplexZero = typeof(System.Numerics.Complex).GetField("Zero", BindingFlags.Public | BindingFlags.Static)
?? throw new MissingFieldException(typeof(System.Numerics.Complex).FullName, "Zero");
public static readonly FieldInfo ComplexOne = typeof(System.Numerics.Complex).GetField("One", BindingFlags.Public | BindingFlags.Static)
?? throw new MissingFieldException(typeof(System.Numerics.Complex).FullName, "One");
public static readonly ConstructorInfo ComplexCtor = typeof(System.Numerics.Complex).GetConstructor(new[] { typeof(double), typeof(double) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, ".ctor(double, double)");
// Complex binary operator methods
public static readonly MethodInfo ComplexOpAddition = typeof(System.Numerics.Complex).GetMethod("op_Addition", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex), typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "op_Addition");
public static readonly MethodInfo ComplexOpMultiply = typeof(System.Numerics.Complex).GetMethod("op_Multiply", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex), typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "op_Multiply");
// Complex unary operator methods
public static readonly MethodInfo ComplexNegate = typeof(System.Numerics.Complex).GetMethod("op_UnaryNegation", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "op_UnaryNegation");
// np.conjugate: Complex.Conjugate(z) flips the sign of the imaginary part (re - i*im).
public static readonly MethodInfo ComplexConjugate = typeof(System.Numerics.Complex).GetMethod("Conjugate", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "Conjugate");
// Sqrt/Exp/Sin/Cos/Tan route through NDComplexMath (not Complex.* directly): the BCL
// matches NumPy on finite interiors but diverges at the C99 edges (non-finite, branch-cut
// signs, signed zeros). NDComplexMath delegates the interior to the BCL and adds the fixups.
public static readonly MethodInfo ComplexSqrt = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Sqrt", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Sqrt");
public static readonly MethodInfo ComplexExp = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Exp", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Exp");
// float32 exp routes through NDFloatMath.Exp (port of NumPy's simd_exp_FLOAT), NOT
// MathF.Exp: the platform libm is ~correctly rounded while NumPy's Remez/Cody-Waite
// kernel carries up to 2.52 ULP of its own error, so "more accurate" reads as a
// divergence. The port is bit-exact against NumPy 2.4.2 over all 2^32 inputs.
public static readonly MethodInfo SingleExp = typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Exp", BindingFlags.Public | BindingFlags.Static, new[] { typeof(float) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDFloatMath).FullName, "Exp");
// The same kernel a whole register at a time, resolved for the width detected at
// startup. NULLABLE by design (unlike the entries above): a host with no SIMD has no
// width to bind, and the eligibility gate (ExpVectorSimdAvailable) reads this field to
// decide, so an absent overload must be a false rather than a type-load failure.
public static readonly MethodInfo SingleExpVector =
VectorBits == 0 ? null
: typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Exp", BindingFlags.Public | BindingFlags.Static,
new[] { VectorMethodCache.V(VectorBits, typeof(float)) });
// float32 exp2 (2^x): NDFloatMath.Exp2, a fast replacement for (float)Math.Pow(2, x). NOT
// a NumPy-kernel port — the win-amd64 wheel runs a scalar exp2f (its SVML vector kernel is
// AVX-512/Linux-gated) — but ≤1 ULP and ~2.4x faster than that scalar loop. Same scalar/
// vector split as SingleExp; the vector overload is nullable for the no-SIMD host.
public static readonly MethodInfo SingleExp2 = typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Exp2", BindingFlags.Public | BindingFlags.Static, new[] { typeof(float) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDFloatMath).FullName, "Exp2");
public static readonly MethodInfo SingleExp2Vector =
VectorBits == 0 ? null
: typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Exp2", BindingFlags.Public | BindingFlags.Static,
new[] { VectorMethodCache.V(VectorBits, typeof(float)) });
// float32 log: the sibling port (NumPy's simd_log_FLOAT), same reasoning as SingleExp.
public static readonly MethodInfo SingleLog = typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Log", BindingFlags.Public | BindingFlags.Static, new[] { typeof(float) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDFloatMath).FullName, "Log");
public static readonly MethodInfo SingleLogVector =
VectorBits == 0 ? null
: typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Log", BindingFlags.Public | BindingFlags.Static,
new[] { VectorMethodCache.V(VectorBits, typeof(float)) });
// float32 sin: port of NumPy's simd_sincos_f32 (loops_trigonometric).
public static readonly MethodInfo SingleSin = typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Sin", BindingFlags.Public | BindingFlags.Static, new[] { typeof(float) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDFloatMath).FullName, "Sin");
public static readonly MethodInfo SingleSinVector =
VectorBits == 0 ? null
: typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Sin", BindingFlags.Public | BindingFlags.Static,
new[] { VectorMethodCache.V(VectorBits, typeof(float)) });
// float32 cos: port of NumPy's simd_sincos_f32 (loops_trigonometric).
public static readonly MethodInfo SingleCos = typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Cos", BindingFlags.Public | BindingFlags.Static, new[] { typeof(float) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDFloatMath).FullName, "Cos");
public static readonly MethodInfo SingleCosVector =
VectorBits == 0 ? null
: typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Cos", BindingFlags.Public | BindingFlags.Static,
new[] { VectorMethodCache.V(VectorBits, typeof(float)) });
// tanh is the FIRST of these ports to cover float64 as well: NumPy ships its own
// kernel (loops_hyperbolic) at both widths, so unlike exp/log/sin/cos — where the
// platform libm already agrees with NumPy at f8 — BOTH the float and double BCL calls
// diverged and both are replaced. Non-nullable: the scalar entry points always exist.
public static readonly MethodInfo SingleTanh = typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Tanh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(float) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDFloatMath).FullName, "Tanh");
public static readonly MethodInfo DoubleTanh = typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Tanh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(double) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDFloatMath).FullName, "Tanh");
// Vector form is float32-only (the f64 lookup would cost 18 gathers per 4 lanes, and the
// scalar f64 kernel already outruns NumPy). Nullable for the usual reason — a host with
// no SIMD has no width to bind.
public static readonly MethodInfo SingleTanhVector =
VectorBits == 0 ? null
: typeof(NumSharp.Utilities.NDFloatMath).GetMethod("Tanh", BindingFlags.Public | BindingFlags.Static,
new[] { VectorMethodCache.V(VectorBits, typeof(float)) });
// ComplexLog routes through NDComplexMath.Log (full npy_clog port): Complex.Log drops the
// real part to 0 near |z|=1 (it lacks clog's log1p path). Reused by the Log2 composition
// and by NDComplexMath.Log10/Log1p.
public static readonly MethodInfo ComplexLog = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Log", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Log");
public static readonly MethodInfo ComplexSin = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Sin", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Sin");
public static readonly MethodInfo ComplexCos = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Cos", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Cos");
public static readonly MethodInfo ComplexTan = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Tan", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Tan");
// Hyperbolic and inverse-trig route through NDComplexMath (not Complex.* directly): the BCL
// matches NumPy only on finite interiors; NDComplexMath adds the C99 Annex G non-finite
// tables and signed-zero/branch-cut fixups so every input matches NumPy.
public static readonly MethodInfo ComplexSinh = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Sinh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Sinh");
public static readonly MethodInfo ComplexCosh = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Cosh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Cosh");
public static readonly MethodInfo ComplexTanh = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Tanh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Tanh");
public static readonly MethodInfo ComplexAsin = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Asin", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Asin");
public static readonly MethodInfo ComplexAcos = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Acos", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Acos");
public static readonly MethodInfo ComplexAtan = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Atan", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Atan");
// Inverse hyperbolic: NumSharp derives casinh/cacosh from the byte-exact Asin/Acos via the
// exact msun involution, and exposes the already-ported catanh (which drives Atan).
public static readonly MethodInfo ComplexAsinh = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Asinh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Asinh");
public static readonly MethodInfo ComplexAcosh = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Acosh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Acosh");
public static readonly MethodInfo ComplexAtanh = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Atanh", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Atanh");
public static readonly MethodInfo ComplexPow = typeof(System.Numerics.Complex).GetMethod("Pow", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex), typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "Pow");
// Log10/Reciprocal/Log1p route through NDComplexMath (Complex.Log10 drifts past 1 ULP from
// NumPy; Complex.op_Division / Complex.One+z drop NumPy's signed zeros).
public static readonly MethodInfo ComplexLog10 = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Log10", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Log10");
public static readonly MethodInfo ComplexReciprocal = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Reciprocal", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Reciprocal");
// Square routes through NDComplexMath (FMA-contracted z*z): Complex.op_Multiply lacks FMA,
// so it loses NumPy's square(1e-10+1e-10i).real = -2.275e-37 and turns the 1e300 overflow
// into NaN instead of NumPy's -inf.
public static readonly MethodInfo ComplexSquare = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Square", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Square");
public static readonly MethodInfo ComplexLog1p = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Log1p", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Log1p");
public static readonly MethodInfo ComplexExp2 = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Exp2", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Exp2");
// Expm1 routes through NDComplexMath (nc_expm1 formula); Complex.Exp(z)-1 mis-handles the
// non-finite imaginary parts (e.g. expm1(+Inf+0i).imag must be NaN = exp(+Inf)*sin(0)).
public static readonly MethodInfo ComplexExpm1 = typeof(NumSharp.Utilities.NDComplexMath).GetMethod("Expm1", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(NumSharp.Utilities.NDComplexMath).FullName, "Expm1");
// Complex doesn't have Log2/Exp2/Log1p directly — composed via Log(z, 2), Pow(2, z),
// NDComplexMath.Log1p in EmitUnaryComplexOperation.
public static readonly MethodInfo ComplexLogBase = typeof(System.Numerics.Complex).GetMethod("Log", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex), typeof(double) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "Log(Complex, double)");
public static readonly MethodInfo ComplexOpSubtraction = typeof(System.Numerics.Complex).GetMethod("op_Subtraction", BindingFlags.Public | BindingFlags.Static, new[] { typeof(System.Numerics.Complex), typeof(System.Numerics.Complex) })
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "op_Subtraction");
// Complex instance property getters — called via Ldloca + Call (struct instance method
// requires a managed reference for 'this').
public static readonly MethodInfo ComplexGetReal = typeof(System.Numerics.Complex)
.GetProperty("Real", BindingFlags.Public | BindingFlags.Instance)!.GetGetMethod()
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "get_Real");
public static readonly MethodInfo ComplexGetImaginary = typeof(System.Numerics.Complex)
.GetProperty("Imaginary", BindingFlags.Public | BindingFlags.Instance)!.GetGetMethod()
?? throw new MissingMethodException(typeof(System.Numerics.Complex).FullName, "get_Imaginary");
// Field handle for the runtime-computed 1/ln(2) constant used by Complex log2 inline IL.
public static readonly FieldInfo LogE_Inv_Ln2Field = typeof(DirectILKernelGenerator)
.GetField(nameof(DirectILKernelGenerator.LogE_Inv_Ln2), BindingFlags.NonPublic | BindingFlags.Static)
?? throw new MissingFieldException(typeof(DirectILKernelGenerator).FullName, nameof(DirectILKernelGenerator.LogE_Inv_Ln2));
// Half unary operator methods. Negate is OUR sign-bit-flip helper, not
// Half.op_UnaryNegation (that operator does a (Half)(-(float)h) roundtrip measured
// 7.3× slower — it made f16 negate the worst cell in the elementwise matrix, ~0.14×).
public static readonly MethodInfo HalfNegate = typeof(DirectILKernelGenerator).GetMethod(nameof(DirectILKernelGenerator.NegateHalf), BindingFlags.NonPublic | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(DirectILKernelGenerator).FullName, "NegateHalf");
public static readonly MethodInfo HalfSqrt = typeof(Half).GetMethod("Sqrt", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Sqrt");
public static readonly MethodInfo HalfSin = typeof(Half).GetMethod("Sin", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Sin");
public static readonly MethodInfo HalfCos = typeof(Half).GetMethod("Cos", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Cos");
public static readonly MethodInfo HalfExp = typeof(Half).GetMethod("Exp", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Exp");
public static readonly MethodInfo HalfLog = typeof(Half).GetMethod("Log", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Log");
public static readonly MethodInfo HalfFloor = typeof(Half).GetMethod("Floor", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Floor");
public static readonly MethodInfo HalfCeiling = typeof(Half).GetMethod("Ceiling", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Ceiling");
public static readonly MethodInfo HalfTruncate = typeof(Half).GetMethod("Truncate", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Truncate");
public static readonly MethodInfo HalfAbs = typeof(Half).GetMethod("Abs", BindingFlags.Public | BindingFlags.Static, new[] { typeof(Half) })
?? throw new MissingMethodException(typeof(Half).FullName, "Abs");
// Sinh/Cosh/Tanh/ASin/ACos/ATan/Asinh/Acosh/Atanh/Tan/Cbrt/Log2/Log10/Exp2/Reciprocal/
// Square on Half now take the float32 fast path (EmitUnaryHalfViaFloat: `(Half)Xf((float)h)`,
// the F16C round-trip that mirrors NumPy's npy_half loop) rather than a BCL Half.* call or
// the double bridge — so the corresponding Half.* MethodInfos (HalfTan/Log10/Log2/Cbrt/Exp2/
// Asinh/Acosh/Atanh) were removed. Only the ops still on a dedicated Half path keep a cached
// method (Sqrt/Sin/Cos/Exp/Log/Floor/Ceiling/Truncate/Abs/Negate above).
// Note: .NET's Half exposes log1p as LogP1 and expm1 as ExpM1 (IFloatingPointIeee754<Half>).
// Half.LogP1/ExpM1 lose subnormal precision because internally they compute (1 + x) in
// Half, which rounds x < Half.Epsilon (≈ 2^-11) to 0. NumPy promotes to a higher-precision
// intermediate before log1p/expm1, then casts back — we replicate that with double
// (via existing HalfToDouble / DoubleToHalf op_Explicit helpers).
public static readonly MethodInfo DoubleLogP1 = typeof(double)
.GetMethod("LogP1", BindingFlags.Public | BindingFlags.Static, new[] { typeof(double) })
?? throw new MissingMethodException(typeof(double).FullName, "LogP1");
public static readonly MethodInfo DoubleExpM1 = typeof(double)
.GetMethod("ExpM1", BindingFlags.Public | BindingFlags.Static, new[] { typeof(double) })
?? throw new MissingMethodException(typeof(double).FullName, "ExpM1");
}
#endregion
/// <summary>
/// Get the Vector container type (Vector128, Vector256, or Vector512).
/// Thin wrapper around <see cref="VectorMethodCache.Container"/> preserved
/// so the existing call sites in the partial-class files keep their shape.
/// </summary>
internal static Type GetVectorContainerType() => VectorMethodCache.Container(VectorBits);
/// <summary>
/// Get the Vector{Width}<T> generic type.
/// Thin wrapper around <see cref="VectorMethodCache.V"/>.
/// </summary>
internal static Type GetVectorType(Type elementType) => VectorMethodCache.V(VectorBits, elementType);
/// <summary>
/// Resolve a NonPublic|Static helper method on <see cref="DirectILKernelGenerator"/> by name.
/// Throws <see cref="MissingMethodException"/> on miss rather than returning <c>null</c>,
/// so renamed or removed helpers fail loudly at IL emit time rather than as NREs later.
/// </summary>
internal static MethodInfo GetHelper(string name)
=> typeof(DirectILKernelGenerator).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static)
?? throw new MissingMethodException(typeof(DirectILKernelGenerator).FullName, name);
/// <summary>
/// Same as <see cref="GetHelper"/> but immediately closes a single generic argument.
/// </summary>
internal static MethodInfo GetGenericHelper(string name, Type genericArg)
=> GetHelper(name).MakeGenericMethod(genericArg);
#region NPTypeCode-Based IL Helpers
/// <summary>
/// Get size in bytes for NPTypeCode.
/// </summary>
internal static int GetTypeSize(NPTypeCode type)
{
return type switch
{
NPTypeCode.Boolean => 1,
NPTypeCode.Byte => 1,
NPTypeCode.SByte => 1,
NPTypeCode.Int16 => 2,
NPTypeCode.UInt16 => 2,
NPTypeCode.Half => 2,
NPTypeCode.Int32 => 4,
NPTypeCode.UInt32 => 4,
NPTypeCode.Int64 => 8,
NPTypeCode.UInt64 => 8,
NPTypeCode.Char => 2,
NPTypeCode.Single => 4,
NPTypeCode.Double => 8,
NPTypeCode.Decimal => 16,
NPTypeCode.Complex => 16,
_ => throw new NotSupportedException($"Type {type} not supported")
};
}
/// <summary>
/// Get CLR Type for NPTypeCode.
/// </summary>
internal static Type GetClrType(NPTypeCode type)
{
return type switch
{
NPTypeCode.Boolean => typeof(bool),
NPTypeCode.Byte => typeof(byte),
NPTypeCode.SByte => typeof(sbyte),
NPTypeCode.Int16 => typeof(short),
NPTypeCode.UInt16 => typeof(ushort),
NPTypeCode.Half => typeof(Half),
NPTypeCode.Int32 => typeof(int),
NPTypeCode.UInt32 => typeof(uint),
NPTypeCode.Int64 => typeof(long),
NPTypeCode.UInt64 => typeof(ulong),
NPTypeCode.Char => typeof(char),
NPTypeCode.Single => typeof(float),
NPTypeCode.Double => typeof(double),
NPTypeCode.Decimal => typeof(decimal),
NPTypeCode.Complex => typeof(System.Numerics.Complex),
_ => throw new NotSupportedException($"Type {type} not supported")
};
}
/// <summary>
/// Check if type supports SIMD operations (V128/V256/V512).
/// </summary>
internal static bool CanUseSimd(NPTypeCode type)
{
if (VectorBits == 0) return false; // No SIMD hardware
return type switch
{
NPTypeCode.Byte or NPTypeCode.SByte => true,
NPTypeCode.Int16 or NPTypeCode.UInt16 => true,
NPTypeCode.Int32 or NPTypeCode.UInt32 => true,
NPTypeCode.Int64 or NPTypeCode.UInt64 => true,
NPTypeCode.Single or NPTypeCode.Double => true,
_ => false // Boolean, Char, Decimal, Half, Complex
};
}
/// <summary>
/// Get vector element count for type (adapts to V128/V256/V512).
/// </summary>
internal static int GetVectorCount(NPTypeCode type)
{
if (VectorBits == 0) return 1; // Scalar fallback
return VectorBytes / GetTypeSize(type);
}
/// <summary>
/// Emit load indirect for NPTypeCode.
/// </summary>
internal static void EmitLoadIndirect(ILGenerator il, NPTypeCode type)
{
switch (type)
{
case NPTypeCode.Boolean:
case NPTypeCode.Byte:
il.Emit(OpCodes.Ldind_U1);
break;
case NPTypeCode.SByte:
il.Emit(OpCodes.Ldind_I1);
break;
case NPTypeCode.Int16:
il.Emit(OpCodes.Ldind_I2);
break;
case NPTypeCode.UInt16:
case NPTypeCode.Char:
il.Emit(OpCodes.Ldind_U2);
break;
case NPTypeCode.Half:
il.Emit(OpCodes.Ldobj, typeof(Half));
break;
case NPTypeCode.Int32:
il.Emit(OpCodes.Ldind_I4);
break;
case NPTypeCode.UInt32:
il.Emit(OpCodes.Ldind_U4);
break;
case NPTypeCode.Int64:
case NPTypeCode.UInt64:
il.Emit(OpCodes.Ldind_I8);
break;
case NPTypeCode.Single:
il.Emit(OpCodes.Ldind_R4);
break;
case NPTypeCode.Double:
il.Emit(OpCodes.Ldind_R8);
break;
case NPTypeCode.Decimal:
il.Emit(OpCodes.Ldobj, typeof(decimal));
break;
case NPTypeCode.Complex:
il.Emit(OpCodes.Ldobj, typeof(System.Numerics.Complex));
break;
default:
throw new NotSupportedException($"Type {type} not supported for ldind");
}
}
/// <summary>
/// Emit store indirect for NPTypeCode.
/// </summary>
internal static void EmitStoreIndirect(ILGenerator il, NPTypeCode type)
{
switch (type)
{
case NPTypeCode.Boolean:
case NPTypeCode.Byte:
case NPTypeCode.SByte:
il.Emit(OpCodes.Stind_I1);
break;
case NPTypeCode.Int16:
case NPTypeCode.UInt16:
case NPTypeCode.Char:
il.Emit(OpCodes.Stind_I2);
break;
case NPTypeCode.Half:
il.Emit(OpCodes.Stobj, typeof(Half));
break;
case NPTypeCode.Int32:
case NPTypeCode.UInt32:
il.Emit(OpCodes.Stind_I4);
break;
case NPTypeCode.Int64:
case NPTypeCode.UInt64:
il.Emit(OpCodes.Stind_I8);
break;
case NPTypeCode.Single:
il.Emit(OpCodes.Stind_R4);
break;
case NPTypeCode.Double:
il.Emit(OpCodes.Stind_R8);
break;
case NPTypeCode.Decimal:
il.Emit(OpCodes.Stobj, typeof(decimal));
break;
case NPTypeCode.Complex:
il.Emit(OpCodes.Stobj, typeof(System.Numerics.Complex));
break;
default:
throw new NotSupportedException($"Type {type} not supported for stind");
}
}
/// <summary>
/// Emit type conversion from source to target type.
/// </summary>
internal static void EmitConvertTo(ILGenerator il, NPTypeCode from, NPTypeCode to)
{
if (from == to)
return; // No conversion needed
// A Boolean's numeric value is exactly 0 or 1 — never its raw storage byte. A bool buffer
// can legally hold non-0/1 bytes (np.frombuffer is a zero-copy VIEW, like NumPy; interop
// wraps a foreign buffer), so normalize nonzero->1 BEFORE widening to any numeric type.
// Without this, sum/mean/var/std over such a buffer accumulate the raw bytes (e.g. byte 255
// contributes 255) instead of counting True. Mirrors the to==Boolean '!= 0' below, applied
// to the source side. Idempotent for proper 0/1 bools (0->0, 1->1).
if (from == NPTypeCode.Boolean) // to != Boolean here (from == to already returned)
{