Hacktoberfest 2026: die Issues, die Maintainer für den Oktober markiert haben – offen und einsteigerfreundlich. Hacktoberfest-Issues durchsuchen

Determinant and Inverse return wrong values when called from more than one thread

Offen
#40 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

@Rafael-SOWNet arbeitet bereits daran.

Seit 09.9.2026.

  • #41 von @Rafael-SOWNet — offen

Bewertung

Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Anfängerfreundlichkeit
35/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Veraltet
Tech-Stack
csharp
Bereich
performance

Rechercherichtung

Beginne mit SquareMatrixFactory<T, TWrapper>.GetMatrix und ExpressionCompiler<T, TWrapper>.storage und reproduziere dann die gemeldeten Fehler bei Determinante und Inversion mit Parallel.For, einschließlich eines kalten Caches. Als abgeschlossen gilt die Aufgabe, wenn nebenläufige Matrixoperationen die sequenziell verifizierten Werte ohne Collection-Ausnahmen oder Beschädigungen gemeinsam genutzter Puffer zurückgeben.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

Two process-wide statics are read and written with no synchronisation, so matrix operations called from more than one thread return wrong numbers. Nothing throws, nothing is malformed, and no result looks suspicious.

I found this from the other side: AngouriMath uses GenTensor for its symbolic matrices, and a determinant computed on a background thread was coming back numerically wrong. AngouriMath's issue is #1219.

1. The scratch-matrix pool

SquareMatrixFactory<T, TWrapper>.GetMatrix hands every caller the same GenTensor for a given size, and the caller then writes into it — Inversion.GetCofactorMatrix(t, temp, ...) fills the matrix it is given. Two threads taking a determinant or an inverse of the same size get the same buffer and overwrite each other's minors between the write and the read.

Sixty 5×5 int matrices, each computed once sequentially for truth and then rebuilt and recomputed under Parallel.For:

DeterminantLaplace    53 of 60 disagree
Adjoint               60 of 60 disagree

Around 10 ms, every run. This is not a rare interleaving, it is the normal case.

DeterminantGaussianSafeDivision is unaffected — it works on its own copy and never asks the pool for anything.

The lock inside GetMatrix does not help and is itself incomplete: the list is indexed after the lock is released, so a concurrent Add can reallocate the backing array underneath the reader.

2. The compiled-operation cache

ExpressionCompiler<T, TWrapper>.storage is a plain Dictionary, written by whichever thread first asks for a given (operation, rank, parallel). Dictionary<,> is documented as safe for concurrent readers only while nobody is writing.

Reached cold from several threads it fails with the runtime's own diagnostic:

System.InvalidOperationException: Operations that change non-concurrent collections must
have exclusive access. A concurrent update was performed on this collection and corrupted
its state. The collection's state is no longer correct.
...
The given key '(Subtraction, 1, False)' was not present in the dictionary.

Worth noting that this one hides easily. My first test computed its expected values sequentially and only then went parallel, which populated every key before the threads started — the cache holds a handful of entries, so a warm-up makes the defect invisible and the test passes against the broken code.

Fix

I have opened a PR. Both are small: [ThreadStatic] for the pool, since a scratch buffer has nothing to share between threads, and ConcurrentDictionary for the cache. The Laplace determinant comes out about 5% faster, because the pool is now taken once at the top of the recursion instead of re-checked and re-indexed at every one of its n! nodes.

Vorherrschende Sprache
C#
Sterne
52
Forks
6
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Entwicklungsumgebung

Dieses Projekt bietet weder Dev-Container noch Dockerfile noch Beitragsleitfaden – die Einrichtung liegt bei Ihnen. Beginnen Sie mit der README; die allgemeinen Schritte stehen in unserem Leitfaden für den ersten Beitrag.

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus ASC-Community/GenericTensor

Alle Issues in ASC-Community/GenericTensor

Ähnliche Issues

Weitere Issues zu C#

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.