-
Notifications
You must be signed in to change notification settings - Fork 0
0001 target framework
Status: accepted · Date: 2026-08-01 · Revised: 2026-08-04
The brief suggested net8.0 (LTS). The development / CI machine has the .NET 10
runtime, so the original decision was net10.0 only, with netstandard2.0
explicitly declined on the grounds that no .NET Framework / Unity consumer had
been identified.
That reasoning has been revisited. The packages are published to nuget.org, where
the consumer is unknown by definition — "no consumer identified" reflects not
having shipped yet, rather than evidence of absence. netstandard2.0 widens reach
to .NET Framework 4.6.1+, Mono, Xamarin and Unity for a bounded, one-off cost.
- The three libraries multi-target
net10.0;netstandard2.0. - Executable projects (tests, benchmarks) stay on
net10.0:netstandard2.0is a contract, not a runtime, and nothing executes on it. - The net10 fast paths are unchanged.
netstandard2.0reaches equivalent behavior through conditional compilation, never a reduced public API — the two builds expose the same surface. - Gaps are closed in three ways, in order of preference: PolySharp compile-time
polyfills; the
System.MemoryandSystem.Numerics.Vectorspackages, referenced only on that target; and hand-written fallbacks where neither applies. - The
RollForwardthat let anet8.0host run on the 10 runtime stays removed.
-
Several
net10.0-onlyVector<T>paths exist in this codebase for the same reason — the span-basedVector<T>constructor is net-only, so each falls back to a scalar loop onnetstandard2.0under#if NET5_0_OR_GREATER—VectorMath.Dotamong them.R2andExplainedVarianceadd one more, for their single-output, unweighted accumulation (issue #127 task 7):VectorCompensatedSumkeeps one Neumaier partial sum per SIMD lane, reduced to oneCompensatedSumat the end.- Unlike the repository's other
Vector<T>paths, this one's two targets are not guaranteed to be bit-identical. Lane-wise summation can reorder the additions relative to the scalar loop (lane 0 sums elements 0, W, 2W, … while lane 1 sums 1, W+1, …), so thenet10.0andnetstandard2.0results forR2/ExplainedVarianceon this path can differ in the last few bits. Both stay Neumaier-compensated and both pass the oracle corpora, which compare at1e-9. This is narrower than the bit-identityPooler.MeanPoolBatchasserts elsewhere in the codebase — a reader should not assume that guarantee extends here.
- Unlike the repository's other
-
A handful of net-only conveniences are replaced by portable equivalents:
ArgumentNullException.ThrowIfNull(behind a sharedGuard.NotNull, so a single#ifcovers every call site instead of one per site),string.Join(char, …),Array.Fill,MathF,CollectionsMarshal,.Order(),KeyValuePairdeconstruction, array range operators, and thestringchar overloads. -
Both builds are behavior-verified. Three mirror test projects replay the entire suite against the netstandard2.0 assemblies, on the net10 runtime, so the assemblies shipped to .NET Framework, Mono and Unity consumers are executed and not merely compiled. The test sources are linked rather than copied, so the two runs can never drift apart.
Each mirror asserts the
TargetFrameworkAttributeof the assembly under test before anything else. Without that, a reference quietly resolving back to net10.0 would leave every test passing while proving nothing — verified by removing the isolation and watching the guard fail. -
CI installs the 10.0.x SDK, which builds both targets.
-
If an LTS-8 consumer appears, add
net8.0toTargetFrameworksrather than downgrading anything.
- 0001-target-framework
- 0002-unicode-comparison-unit
- 0003-provenance-and-licensing
- 0004-levenshtein-myers-backlog
- 0005-hamming-jellyfish-divergence
- 0006-ratcliff-autojunk
- 0007-metaphone-scope
- 0008-italian-enza-nltk-divergence
- 0009-sample-consumes-a-local-feed
- 0010-stop-word-list-provenance
- 0011-persistence-format
- 0012-per-package-versioning
- 0013-sentencepiece-parity-scope
- 0014-precompiled-normalizer
- 0015-sonar-rules-in-the-build
- 0016-metrics-package-placement
- 0017-bpe-parity-scope
- 0018-multiclass-roc-auc-parallelism-is-opt-in
- 0019-the-net-analysers-run-in-the-build-too
- 0020-normalize-is-a-projection-not-a-parameter
- 0021-multioutput-is-a-method-not-an-enum
- 0022-added-token-matching-flags
- 0023-byte-level-decode-substitutes
- 0024-weighted-median-averages-within-scikit-learns-epsilon
- 0025-quickselect-replaces-a-full-sort-for-the-median
- 0026-r2-and-explainedvariance-split-their-undefined-cases-differently
- 0027-r2-and-explainedvariance-vectorize-only-a-single-output
- 0028-log1p-is-kahans-identity-not-math-log-1-plus-x
- 0029-balanced-accuracy-adjusted-is-left-to-ieee-754-at-the-edge
- 0030-cohen-kappa-keeps-scikit-learns-expected-matrix-orientation
- 0031-nosamplecorrect-mirrors-numpys-float64-upcast
- 0032-fbeta-substitutes-tp-predicted-and-support-algebraically
- 0033-compensated-sum-is-neumaiers-variant
- 0034-dropout-is-refused-for-want-of-a-user
- 0035-a-null-pre-split-is-removed-with-invert-not-isolated
- 0036-a-member-may-ship-without-an-oracle-if-it-says-so
- 0037-the-guards-run-before-the-commit
- 0038-the-gate-confronts-an-exception-tag-with-the-page-that-documents-it
- 0039-mutual-information-returns-zero-on-an-empty-input
- 0040-a-curve-is-a-sealed-class-per-curve
- 0041-one-sample-file-per-public-class
- 0042-phonetic-encoders-refuse-a-null-word
- 0043-the-equality-table-is-sized-to-the-pattern
- 0044-compression-belongs-to-the-caller
- 0045-a-console-call-carries-its-reason-on-the-line
- 0046-check-adr-immutable-runs-in-ci-only
- 0047-one-gate-per-kernel-not-one-per-alphabet
- 0048-the-gate-depends-on-the-kernel-and-the-alphabet
- 0049-two-gates-per-kernel-tested-where-the-width-is-known
- 0050-the-sentencepiece-bpe-lineage-stays-a-bpe-model
- benchmark_latest
- decisions
- equivalence
- matplotlib
- migration
- nightly_run
- numpy
- pandas
- performance
- pytorch
- seaborn
- sklearn
- statsmodels