Repository navigation
RMC.Numerics v2.3.0 adds regional frequency analysis, new machine learning and optimization methods, Linux test coverage, and the pynumerics Python package on PyPI. It also corrects a set of accuracy defects in special functions, distributions and copulas, and drops the net8.0 and net9.0 targets.
Highlights
- Regional frequency analysis. The new
Numerics.Distributions.Regionalnamespace implements the Hosking-Wallis method:RegionalData(site L-moment ratios, regional averages, discordancy and its critical values),RegionalHomogeneityTest(H1, H2 and H3 heterogeneity and the Z goodness-of-fit measures from seeded, thread-count-independent kappa simulations) andRegionalGrowthCurve(growth factors and site quantiles). - Machine learning.
GaussianProcessRegression,Kriging,ElasticNetRegressionandPrincipalComponentAnalysis. - Optimization.
LBFGSB(L-BFGS-B 3.0),CMAESwith IPOP restarts,DynamicallyDimensionedSearch,ParetoFront,DynamicProgrammingandKnapsack, plus cancellation and progress reporting on everyOptimizer. - Numerical methods. A QL eigensolver (
EigenSolverMethod.QL), QUADPACK QAGS inAdaptiveGaussKronrod(opt-in), a relative-toleranceBrent.Solve, and multi-source CSRDijkstra.ShortestDistances. - Accuracy. StudentT, the incomplete beta and inverse incomplete gamma functions, the Archimedean copula densities, and the GEV, Gamma and Pearson Type III L-moments are corrected against mpmath, SciPy and published reference implementations.
- pynumerics. A Python package that ships this
Numerics.dllwith a CoreCLR loader and typed wrappers over every Numerics area. It is published to PyPI aspynumericsautomatically after each NuGet release. - Linux. The
net10.0build and pynumerics are tested on Linux (Ubuntu 24.04) as well as Windows. The.github/docker/linux-testimage runs the .NET tests, the wheel build, pytest and an identity check of the installed assembly.
Breaking changes
Target frameworks
RMC.Numerics 2.3.0 targets net10.0 and net481 only. The net8.0 and net9.0 builds are no longer shipped, because Microsoft ends support for .NET 8 (LTS) and .NET 9 (STS) on November 10, 2026 (.NET support policy). Applications that run on .NET 8 or .NET 9 can stay on RMC.Numerics 2.2.0. Package validation against 2.2.0 reports no API breaks; the only suppressions are for the two dropped frameworks.
Members that now throw instead of returning an invalid result
GeneralizedExtremeValue.ParametersFromLinearMomentsthrowsArgumentOutOfRangeExceptionwhen λ1 or λ2 is not finite, λ2 ≤ 0, or |τ3| ≥ 1. In 2.2.0 it returned parameters for such input.Estimate(sample, ParameterEstimationMethod.MethodOfLinearMoments)reaches this check for constant data.DijkstraandNetworkthrowArgumentOutOfRangeExceptionat entry when any edge weight is negative. In 2.2.0 a negative cycle never terminated. NaN and infinite weights still cut an edge in the edge-list solvers, and graphs with non-negative weights give bitwise-identical results. In pynumerics the exception surfaces asValueError.- The weighted plotting-position
Statistics.Percentile(new in this release) throwsArgumentExceptionwhen the data hold NaN. The 2.3 pre-release builds accepted NaN there. Deterministic.PDF,CDFandInverseCDFthrowArgumentOutOfRangeExceptionwhenValueis NaN or infinite, as the other distributions do. In 2.2.0 they returned a value.
Other behaviour changes
Tools.LogSumExp(IList<double>)returns +inf when any value is +inf (2.2.0 returned NaN) and -inf for an empty list (2.2.0 returned NaN), followingscipy.special.logsumexp. NaN still propagates.GoodnessOfFit.PBIASfollows Moriasi et al. (2007), 100 Σ(O - M) / Σ(O), positive when the model underestimates, as its documentation already stated. Results change sign relative to 2.2.0.Gamma.InverseLowerIncompleteandGamma.InverseUpperIncompletereturn NaN for invalid input (a shape or probability outside the domain), where 2.2.0 threwArithmeticExceptionorArgumentOutOfRangeException. The edge values are corrected:InverseLowerIncomplete(a, 0)is 0 andInverseLowerIncomplete(a, 1)is +inf.GoodnessOfFit.KolmogorovSmirnovandGoodnessOfFit.AndersonDarlingcompute the statistic on a sorted copy, so unsorted input gives the correct statistic (2.2.0 silently returned a wrong value for unsorted input; sorted input gives the same result bit for bit). New overloads takebool dataIsSorted, asStatistics.Percentiledoes:trueuses the values as given and skips the copy.TimeSeriesDownloadskips a USGS peak whose value is not a number instead of storing it as 0, so a non-numeric peak no longer enters an annual-maximum series as a zero.AdaptiveSimpsonsRulestops withIntegrationStatus.Failurewhen an accepted interval has a NaN or infinite estimate, and throwsArithmeticExceptionwhenReportFailureis true. In 2.2.0 it spent the remaining budget and returned a non-finite result.
Reliability and Fixes
Special functions
- StudentT. In 2.2.0 the CDF switched to the Normal for ν ≥ 1e8, with far-tail relative error up to 3.8e-3. The tails now use the large-shape incomplete beta expansion from ν = 30, with log-space tails, the Cauchy closed form at ν = 1, and a log-space InverseCDF in the tails, so extreme probabilities at very large ν converge. Two-sided t-test p-values use the incomplete beta tail instead of 2(1 - CDF).
- Beta.Incomplete. Where one shape is at least 15 and the other at most 1, the continued fractions converged too slowly (relative errors of 1.5e-7 to 1.4e-13 on the oracle grid). These cases now use the BGRAT expansion of Algorithm 708, and shapes below 0.1 use the BUP recurrence with BGRAT.
Beta.IncompleteInverserefines small-tail roots in log space, returns subnormal or zero roots instead of throwing, and no longer cycles when the reflected target rounds to one.Beta.LogFunctionuses BETALN, ALGDIV and BCORR, accurate to a few ulps where log-gamma differences lose digits. - Inverse incomplete gamma. One bracketed Halley solver now serves
Gamma.InverseLowerIncomplete,Gamma.InverseUpperIncomplete,GammaDistribution,PearsonTypeIIIand theMultivariateStudentTradial factor. It returns a value for every shape and probability, including tiny-shape quantiles far below one, and returns 0 only below the smallest double. On a 375-root oracle grid the relative error is 1.8e-15 for a ≥ 1 and 2.8e-14 below. - Tools.Log1p. The compensated form now applies at every finite argument above -1. The direct log(1 + x) used above |x| = 1e-4 lost up to about 1e-12 relative accuracy.
- Gamma.Trigamma is accurate to full double precision.
Statistics
- Variance and covariance.
Statistics.Variance,Covarianceand their weighted overloads use the corrected two-pass sum. With a mean offset of 1e6 the relative error falls from 1.4e-6 to 1.2e-12. - Pearson correlation.
Correlation.Pearson(list and matrix overloads) uses the corrected two-pass sums. On data offset by 1e9 the error falls from 1166 ulp to 0.4 ulp. - Fourier.Autocorrelation is the linear autocorrelation. It padded only to the next power of two, so wrapped-around products entered every lag after lag 0.
- LinearRegression.PredictionIntervals include the leverage term x0ᵀ(XᵀX)⁻¹x0, so intervals widen away from the data.
- Correlation.KendallsTau compares values, uses 64-bit counts and returns NaN for NaN input.
Distributions
- GEV L-moments.
ParametersFromLinearMomentsfollows Hosking's PELGEV with one Newton refinement; 2.2.0 used an approximation accurate to about 9e-4 for |τ3| ≤ 0.5, and the shape error is now about 3e-14.LinearMomentsFromParametersreturned NaN at k = 0 and now returns the exact Gumbel value; near k = 0 its error falls from up to 7.9e-4 to 5e-15. - Gamma L-moments. L2 is finite for shapes above 171. In 2.2.0 the gamma-function ratio overflowed and returned infinity or NaN.
- Pearson Type III and log-Pearson Type III. The L-moment L2 and the L-moment fit used an asymptotic form for α ≥ 100 (small skew), with an error and a jump of 4.9e-9 at the switch. They now use the exact gamma ratio.
- GeneralizedPareto.ParametersFromMoments solves location, scale and shape by the method of moments. In 2.2.0 it used a two-parameter formula and left the shape at zero.
- Mode.
KernelDensity.ModeandTruncatedDistribution.Modecould return a local, non-global peak; they now search a global grid and polish the highest peak. The pinned bimodal KDE case moves from 1.996 to -1.9665, the taller peak.EmpiricalDistribution.Modereturns the largest point mass when there is one, otherwise the midpoint of the highest bin. - EmpiricalDistribution with the NormalZ transform. The CDF returned NaN at the node next to p = 1; interpolation toward a segment end at infinity now returns the limit.
- TruncatedDistribution XML.
ToXElementwrites the truncation bounds (TruncationMinandTruncationMax), and the factory restores them, including insideMixtureandCompetingRiskscomponents. In 2.2.0 an XML round trip dropped the truncation. - EmpiricalDistribution.Clone and XML keep descending (survival) and tied-probability tables. In 2.2.0 they became ascending, strictly increasing tables that failed validation. Existing XML reads unchanged, and default settings write the same XML.
Copulas
- Joe. The density was off by up to 11% at θ = 2 and underflowed at large θ. It is now evaluated in log space.
- Clayton returned NaN for θ < 0, and Frank overflowed for θ above about 236. Both are fixed and continuous through θ = 0.
- Gumbel. The density is evaluated in log space (values move by up to 3e-10). The Gumbel and Joe
ConditionalCDFuse closed forms; they lost up to 1e-5 before. - InverseConditionalCDF returns the exact limits at u = 0 and u = 1 for the Gumbel, Joe, Normal, Student-t, Clayton and AMH copulas. Gumbel and Joe are inverted at full precision.
- AMH InverseConditionalCDF returned NaN at θ = 0 (independence), where it now returns v = t, and it returns exact limits at t = 0 and t = 1.
- AMH at θ = 1. The generator is identically zero there, so
CDFandConditionalCDFuse the closed form at θ = 1.
Numerical methods
- Dijkstra and Network no longer loop without end on a negative cycle (see Breaking changes).
- RungeKutta. The Fehlberg error term weighted k6 instead of k5, which inflated the error estimate; adaptive steps can now grow, and Cash-Karp steps use the fifth-order solution.
- Optimizer budgets.
PowellthrewNullReferenceExceptionwhen the evaluation budget ran out inside a line search; it now ends with its normal budget stop.MultiStartandMLSLgive each local search at least 10 evaluations and never exceedMaxFunctionEvaluations, withReportFailuretrue or false.
Data
- TimeSeriesDownload.FromUSGS uses the USGS Water Data API v1, because the legacy NWIS WaterServices retire on February 22, 2027.
- GHCN DailySnow reads snowfall in its native millimeters. In 2.2.0 non-zero snow depths were 10 times too small.
API additions
Statistics (Numerics.Data.Statistics)
- Column statistics over the finite entries of each column, each with an
out int[] excludedoverload:Statistics.ColumnMeans,ColumnVariances,ColumnStandardDeviations,ColumnLinearMoments(4 × p) andColumnPercentiles(Type 7 and plotting-position forms). Each column is bitwise the one-sample member. GoodnessOfFit.ColumnKolmogorovSmirnovUniformandColumnAndersonDarlingUniform: one sorted sample per column against Uniform(0, 1).- Plotting-position percentiles:
Statistics.Percentile(data, k, PlottingPostionType, dataIsSorted), its list-of-k overload, and the weighted formsPercentile(data, k, weights, PlottingPostionType, dataIsSorted)with a list-of-k overload. PlottingPositions.Alpha(PlottingPostionType), and weightedPlottingPositions.Function(weights, alpha)andFunction(weights, PlottingPostionType).Statistics.LinearMoments(data, nmom)for any number of L-moments (its first four values are bitwise the four-value member),Statistics.Median,Statistics.LinearCoefficientOfVariationandStatistics.WilsonScoreInterval.Autocorrelation.Function(data, lagMax, type, centered)and itsTimeSeriesoverload:centered: falsegives the autocorrelation about zero. The 2.2.0 signatures are unchanged.HypothesisTests.TwoSampleKolmogorovSmirnov(exact),HypothesisTests.HartiganDip, and an opt-in parametric-bootstrapUnimodalityTest(sample, realizations, seed).GoodnessOfFit.KolmogorovSmirnov(observedValues, model, dataIsSorted)andGoodnessOfFit.AndersonDarling(observedValues, model, dataIsSorted):trueuses the values as given and skips the sorted copy.
Tools (Numerics.Tools)
Tools.CompensatedSum(plain, indicator and gather overloads) andTools.CompensatedCumulativeSum, each with akahanoption. Neumaier summation is the default.Tools.KahanAdd,Tools.NeumaierAddandTools.NeumaierResult: the compensated step for callers' own loops.Tools.LogSumExp(double[,] values, int axis).Tools.Hypot, and list forms ofTools.Expm1andTools.Log1p.
Distributions (Numerics.Distributions)
- Per-element static members:
Poisson.InverseCDF(rates, probabilities),Poisson.LogPDF(rates, k),Poisson.CDF(rates, k),Binomial.LogPDF(successProbabilities, trials, k)andBinomial.CDF(successProbabilities, trials, k). Each element is bitwise the instance member. Multinomial.CategoricalIndices(cumulativeWeights, uniforms)and the overload withinclusive, which selects min{k : F(k) ≥ u}.GeneralizedPareto.FromPoissonGeneralizedExtremeValueandGeneralizedExtremeValue.FromPoissonGeneralizedPareto.GeneralizedPareto.MLE(sample, location)and theIFixedParameterEstimationinterface for fits with known parameters.UnivariateDistributionBase.ParameterSetQuantiles(virtual;GeneralizedExtremeValueoverrides it), and list forms ofCCDF,LogPDF,LogCDFandLogCCDF.- List forms of the bivariate copula
PDF,CDF,ConditionalCDFandInverseConditionalCDF. - A weighted
UncertaintyAnalysisResults.ProcessMeanCurve. EmpiricalDistribution.StrictXValuesandStrictProbabilities.MultivariateStudentT.CDFuses the Genz-Bretz method, withAbsoluteError,RelativeErrorandMaxEvaluations.Numerics.Distributions.Regional:RegionalData,RegionalHomogeneityTestandRegionalGrowthCurve.
Numerical methods (Numerics.Mathematics)
EigenSolverMethod(Jacobi,QL) with theEigenValueDecomposition(Matrix A, EigenSolverMethod method)constructor. QL returns ascending eigenvalues, asnumpy.linalg.eighdoes. The one-argument constructor is unchanged.AdaptiveGaussKronrodMethod(LocalBisection,Qags) and theAdaptiveGaussKronrod.Methodproperty. The default staysLocalBisection.Brent.Solve(f, lowerBound, upperBound, absoluteTolerance, relativeTolerance, maxIterations, reportFailure)andBrent.MinimumRelativeTolerance.Dijkstra.ShortestDistances: double-precision multi-source shortest distances on a CSR graph, with optional predecessors and nearest sources.CholeskyDecomposition.TryCholesky(non-throwing, with escalating diagonal jitter) andMatrix.TransposeMultiply.Fourier.Convolve(real linear convolution) andFourier.BluesteinFFT(any-length FFT).Bessel.I0eandBessel.I1e, andEvaluate.Chebyshev.- Optimizers:
LBFGSB,CMAES,DynamicallyDimensionedSearch,ParetoFront,DynamicProgramming,DynamicProgrammingPathandKnapsack. Optimizer.CancellationToken,Optimizer.Progress(OptimizationProgress) andProgressInterval.LocalMethod.LBFGSBforMultiStartandMLSL.DifferentialEvolution.InitialPopulationandIntegerParameters, andUseParallelonDifferentialEvolutionandParticleSwarm.BFGS.ScaleInitialInverseHessian.
Machine learning (Numerics.MachineLearning)
GaussianProcessRegression, with squared exponential, Matérn 3/2 and 5/2 and exponential kernels with ARD (GaussianProcessKernel,GaussianProcessMean).Kriging(KrigingType,VariogramModel).ElasticNetRegression: the Gaussian elastic net path by covariance-update coordinate descent, following glmnet, with k-fold cross-validation.PrincipalComponentAnalysis(PrincipalComponentBasis).
Sampling and data
NUTS.LogLikelihoodAndGradientFunction: an opt-in combined value-and-gradient delegate.LatinHypercube.RandomandMedianoverloads that take a generator.TimeSeries.AggregateByInterval.TimeSeriesDownload.ParseUSGS,ParseGHCN,ParseCHMNandParseABOMfor saved responses, with opt-in qualifier overloads.
pynumerics
python/pynumerics is a new Python package, published to PyPI as pynumerics. It ships the Numerics.dll from this NuGet release byte for byte (SHA-256 checked), with one CoreCLR loader per process, bulk bit-exact NumPy marshalling, Python exceptions that carry the Numerics messages, and typed wrappers over every Numerics area. The modules include distributions, multivariate, copulas, regional, statistics, hypothesis_tests, goodness_of_fit, sampling, mcmc, bootstrap, integration, differentiation, roots, ode, optimization, graphs, special, linalg, mathematics, functions, timeseries, download and machine_learning. It requires CPython 3.10 to 3.14 on Linux or Windows and the .NET 10 runtime. See python/pynumerics/README.md, the guides in python/pynumerics/docs and python/pynumerics/CHANGELOG.md.
.github/workflows/PythonPublish.yml starts after NuGetPublish.yml has published RMC.Numerics to nuget.org. It builds the wheel from the nuget.org package, writes the package's version, assembly version and SHA-256 into it, tests it on Linux and Windows, and publishes it to TestPyPI and then, after approval, to PyPI with trusted publishing. Pull requests build the wheel from the PR source and run the same tests, including the Linux Docker image.
Performance
Measured speeds, with their conditions. Unless noted, results are bitwise unchanged.
- QL eigensolver. SIMD tred2 and blocked tql2 rotations, applied by parallel tasks from n = 128, with the same bits as the sequential EISPACK procedures. On random Gram matrices (.NET 10) QL takes 3.4-3.7 ms at n = 200 and 47-49 ms at n = 500. Jacobi takes 165 ms and 3.3 s.
numpy.linalg.eighwith its default threads takes 5.8-7.1 ms and 196-214 ms on the same machine. With one BLAS thread, eigh is faster from n = 100.PrincipalComponentAnalysisshares the tridiagonal path. - Poisson arrays. Through pynumerics,
poisson_inverse_cdfis 3.9 times faster thanscipy.stats.poisson.ppfat n = 200, 15 times at 1e4 and 31 times at 2e5, with no mismatches on 210,200 draws. The static array members run in parallel from 4096 elements. For short arrays in hot loops, the per-call cost makes the pynumerics log-mass wrappers slower than local NumPy code. - Dijkstra.ShortestDistances. Ten sources, five edges per node, against
scipy.sparse.csgraph.dijkstra(min_only=True): 1.03 ms vs 1.65 ms at 1e4 nodes, 15.4 ms vs 24.6 ms at 1e5, and 473 ms vs 624 ms at 1e6 in .NET. Through pynumerics the times are 1.17, 17.8 and 573 ms. With thousands of sources, pynumerics and SciPy run at about the same speed end to end. - Tools.Expm1. For |x| > 1/2 it takes the direct difference, which is about 2.8 times as fast on .NET 10 and 1.9 times on .NET Framework 4.8.1, and more accurate (at most 1.06 ulp on (0.5, 10], down from 2.02 ulp).
- Uncentered autocorrelation. Each lag sum is divided once by the lag-zero sum, and four lags accumulate per sweep of a contiguous array. The centered path is unchanged.
- Column statistics. Parallel above a measured work threshold, with direct array access. Through pynumerics the batched calls are 16 to 30 times faster than per-column calls. Column L-moments and Anderson-Darling match or beat NumPy; column variance, standard deviation and Kolmogorov-Smirnov stay slower than NumPy at 60 × 2000, because copying the matrix into .NET alone costs more than NumPy's computation.
- pynumerics marshalling. Arrays cross through the .NET array buffer protocol, and overloads and constructors are bound once. One-argument array callbacks add about 0.4 µs over raw pythonnet.
Numerical results that move
These members now return slightly different, more accurate values. Signatures are unchanged.
- Root finding and optimization.
Brent.Solveis a port of SciPy's brentq; its relative stopping term is 4.4e-16|x| (was 2.2e-16|b|), so roots move within tolerance.BrentSearchis a port of SciPy's bounded minimizer and starts at the golden-section point; iterates move at tolerance level. Fits that call either one internally (copulas, GEV, GPD, Mixture, NoncentralT, TruncatedNormal and others) move within tolerance.PowellandMultiStartfingerprints move by up to about 3e-9. - LogSumExp. Uses SciPy's evaluation (separated maximum, log1p and a Neumaier sum). Finite results move by a few ulp, and
NUTStree log-weights move in the last bits, so NUTS chains are not bitwise to 2.2.0. - GEV and GPA fits. L-moment fits, and the MLE and Bayesian starting values seeded from them, move. GPA method-of-moments fits move.
- Pearson Type III and log-Pearson Type III.
- The L-moment L2 and fit σ move by up to 4.9e-9 for |skew| ≤ 0.2.
- InverseCDF and QuantileGradient use an order-8 Cornish-Fisher series for |skew| ≤ 0.03: quantiles move by up to about 3e-12 in standardized units, and gradients by up to 9.3e-8 relative.
- The centered tails apply below skew 0.1, so LogCDF, LogCCDF and PDF move by up to 1.8e-13 for 0.01 ≤ |skew| < 0.1.
- Log-Pearson Type III Mean, Variance and SD move by up to 5.1e-14 relative.
GammaDistribution.FrequencyFactorKpno longer snaps to z for |C| < 1e-4, so Wilson-Hilferty quantiles move by up to 3.6e-4 in that band.
- Gamma-family quantiles and gradients. These move through the inverse incomplete gamma, Trigamma and the log-tail shape derivative: Gamma, Pearson Type III and log-Pearson Type III InverseCDF, QuantileGradient and maximum-likelihood covariances, Dirichlet sampling, and the multivariate t CDF.
- StudentT and Beta. StudentT CDF and InverseCDF move for ν ≥ 30, which reaches t-tests,
LinearTrendTestand regression p-values. Beta-family values move in the regions where the incomplete beta and log-beta changed. - Variance, covariance and Pearson. These move by about 1e-14 on ordinary data.
- Percentiles. Plotting-position percentiles with Weibull, Blom and Hazen weights move by up to 12 ulp. Median, Cunnane and Gringorten are unchanged.
- Copulas.
- Joe, Gumbel, Clayton and Frank densities: Gumbel moves by up to 3e-10, Clayton and Frank by about 1e-14, and Joe by the corrections above.
- Copula likelihood fits move.
- Seeded draws from Gumbel and Joe move v by up to 2.5e-9; u is unchanged.
- The Clayton generator follows Nelsen's normalized form, so its outputs are scaled by 1/θ.
- Tools.Expm1 and Tools.Log1p callers. Expm1 for |x| > 1/2 and Log1p for |x| > 1e-4 move at the ulp level. This reaches about 116 call sites, including the copulas, KappaFour, GEV, GPD, the lognormal family, Exponential, Gumbel, Weibull, Pareto and Rayleigh, and the gamma family through
Log1mExp. - Mode. KDE and Truncated modes move to the global peak. For unimodal bases,
TruncatedDistribution.Modereturns the closed-form mode, moving by up to 7.5e-8 absolute toward the exact value. - Linear algebra.
CholeskyDecomposition,Solveand the inverse use a blocked kernel for n ≥ 16; values move by more than 1e-12 relative only for ill-conditioned matrices (κ above about 1e5). Below n = 16 every bit is unchanged.GaussianMixtureModelmoves by at most 2.3e-13 relative at dimension 16 and above, and its M-step computes the covariances about the updated means, which moves results in the last bits.ElasticNetRegressionmoves in the last bits on hardware with FMA.
- Other.
Fourier.Autocorrelation(every lag after 0),GoodnessOfFit.PBIAS(sign),LinearRegression.PredictionIntervalsand theRungeKuttaFehlberg and Cash-Karp steps change as described under Reliability and Fixes.
Unchanged:
- The Bulletin 17C conditional moments (
PearsonTypeIII.ConditionalMoments) are bitwise unchanged, as checked on a 104,196-record pin harness. Bulletin 17C integration bounds move by at most 1.8e-12. - The default
AdaptiveGaussKronrodpath (LocalBisection) is bitwise unchanged. QAGS runs only whenMethod = AdaptiveGaussKronrodMethod.Qagsis set. - The Poisson and Binomial instance members, the compensated sums and the QL and Dijkstra speed work are bitwise unchanged.
GoodnessOfFit.KolmogorovSmirnovandAndersonDarlingare bitwise unchanged for ascending input.
Platforms: .NET math functions such as Math.Exp, Log, Sin and Pow call the platform C library, which differs from Windows by about one ulp on some inputs. Results on Linux therefore agree with Windows closely but not bit for bit (the tests measured differences of 1 to 423 ulp, at most about 1e-13 relative), and seeded MCMC chains can diverge after many steps while keeping the same distribution. The tests record exact references on Windows and state their tolerance on other platforms.
Install
dotnet add package RMC.Numerics --version 2.3.0Python (CPython 3.10 to 3.14 on Linux or Windows, with the .NET 10 runtime installed):
pip install pynumerics==2.3.0