====== std.tensor — komplexe Tensoren und Quantenzustände ======
Tensoren mit allgemeiner Kontraktion über beliebige Achsen; darauf aufgesetzt Quantenzustände, Gatter, reduzierte Dichtematrix und Verschränkungsentropie.
→ [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] · [[lyx_-_programmiersprache:units:complex|std.complex]] · [[lyx_-_programmiersprache:units:linalg|std.linalg]] · [[lyx_-_programmiersprache:units:fft|std.fft]]
import std.tensor;
Schicht 0, hängt an ''std.math'' und ''std.alloc''. 40 Funktionen. Alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt.
----
===== Darstellung =====
Ein komplexer Tensor sind **zwei** ''f64''-Felder — Realteil und Imaginärteil, getrennt und gleich lang. Die Form beschreibt ein ''int64''-Feld ''shape'' mit ''rank'' Einträgen; ''TenSize(shape, rank)'' liefert die Gesamtzahl der Elemente.
^ Funktion ^ Zweck ^
| ''TenSize(shape, rank)'' | Elementzahl aus der Form |
| ''TenStrides(shape, rank, outStrides)'' | Schrittweiten je Achse |
| ''TenFlatIndex(strides, idx, rank)'' | Mehrfachindex → flache Position |
| ''TenUnflatten(flat, shape, rank, outIdx)'' | und zurück |
Rechnen: ''TenAdd'', ''TenSub'', ''TenScale'', ''TenConj'', ''TenCopy'', ''TenZero'', ''TenNorm'', ''TenNormSq'', ''TenInner''.
Umformen und Verknüpfen: ''TenPermute'' (Achsen tauschen), ''TenOuter'' (äußeres Produkt), ''TenContract'' (Kontraktion über gemeinsame Achsen), ''TenPartialTrace'' (Teilsystem ausspuren). ''TenContractRank(rankA, rankB, nAxes)'' sagt vorab, welchen Rang das Ergebnis hat — nützlich, um den Zielpuffer richtig zu bemessen.
----
===== Quantenzustände =====
Ein Zustand aus ''nQubits'' Qubits ist ein Vektor der Länge ''2^nQubits''; ''QuStateSize'' rechnet das aus. Ein Gatter ist selbst ein kleiner Tensor, den die ''QuGate*''-Funktionen füllen — ''QuApply1'' wendet ein Einzelqubit-Gatter an, ''QuApply2'' ein Zweiqubit-Gatter.
unit main;
import std.io;
import std.string;
import std.alloc;
import std.tensor;
fn ZF(t: pchar, v: f64): void { Print(t); PrintF64(v); }
fn ZI(t: pchar, v: int64): void { Print(t); PrintLn(IntToStr(v)); }
fn main(): int64 {
var nq: int64 := 2;
var sz: int64 := QuStateSize(nq);
ZI("StateSize(2 Qubits) : ", sz);
var re: int64 := alloc(sz * 8);
var im: int64 := alloc(sz * 8);
QuStateZero(re, im, nq); // |00>
var g: int64 := alloc(4 * 2 * 8);
var gi: int64 := alloc(4 * 2 * 8);
QuGateH(g, gi);
QuApply1(re, im, nq, 0, g, gi); // Hadamard auf Qubit 0
var c: int64 := alloc(16 * 2 * 8);
var ci: int64 := alloc(16 * 2 * 8);
QuGateCNOT(c, ci);
QuApply2(re, im, nq, 0, 1, c, ci); // CNOT -> Bell-Zustand
ZF("P(|00>) : ", QuProbability(re, im, nq, 0));
ZF("P(|01>) : ", QuProbability(re, im, nq, 1));
ZF("P(|10>) : ", QuProbability(re, im, nq, 2));
ZF("P(|11>) : ", QuProbability(re, im, nq, 3));
ZF("P(Qubit0 = 1) : ", QuProbQubitOne(re, im, nq, 0));
ZF("Norm : ", TenNorm(re, im, sz));
ZF("Entropie Qubit 0 : ", QuEntanglementEntropy(re, im, nq, 0));
return 0;
}
StateSize(2 Qubits) : 4
P(|00>) : 0.499999
P(|01>) : 0.000000
P(|10>) : 0.000000
P(|11>) : 0.499999
P(Qubit0 = 1) : 0.499999
Norm : 0.999999
Entropie Qubit 0 : 1.000000
Das ist der **Bell-Zustand**: die beiden Qubits sind entweder beide 0 oder beide 1, je zur Hälfte — die gemischten Ergebnisse haben Wahrscheinlichkeit null. Die Norm bleibt 1, wie sie es bei unitären Gattern muss; das ist zugleich eine referenzfreie Prüfung, dass die Gatter richtig angewandt wurden.
> **Die Entropie ist ''1'', nicht ''0,693''.** ''QuEntanglementEntropy'' rechnet in **Bit**, nicht in Nats: ''0'' heißt unverschränkt, ''1'' maximal verschränkt. Der Bell-Zustand ist maximal verschränkt, also genau 1.
>
> Die Quelle nennt die Genauigkeit selbst mit **rund 1e−7**, geerbt von ''LogF64''. Das genügt, um verschränkt von unverschränkt zu unterscheiden — **nicht** für feine Vergleiche benachbarter Zustände.
^ Gruppe ^ Gatter ^
| Einzelqubit | ''QuGateH'' · ''QuGateX'' · ''QuGateY'' · ''QuGateZ'' · ''QuGateS'' · ''QuGateT'' |
| Drehungen | ''QuGateRX'' · ''QuGateRY'' · ''QuGateRZ'' (Winkel in Radiant) |
| Zweiqubit | ''QuGateCNOT'' · ''QuGateCZ'' · ''QuGateSWAP'' |
^ Funktion ^ Zweck ^
| ''QuProbability(re, im, nQubits, basis)'' | Wahrscheinlichkeit eines Basiszustands |
| ''QuProbQubitOne(…, qubit)'' | Wahrscheinlichkeit, dass ein einzelnes Qubit 1 ist |
| ''QuReducedDensity(…, qubit, outRe, outIm)'' | reduzierte Dichtematrix (2×2) |
| ''QuEntanglementEntropy(…, qubit)'' | Von-Neumann-Entropie in Bit |
| ''QuNormalize'' | Zustand wieder auf Norm 1 bringen |
| ''QuStateZero'' / ''QuStateBasis'' | Anfangszustand setzen |
----
===== Grenzen =====
> **Der Speicher wächst exponentiell.** ''2^n'' komplexe Zahlen zu je 16 Byte: 20 Qubits sind 16 MB, 30 Qubits 16 GB, 40 Qubits nicht mehr darstellbar. Das ist keine Eigenheit dieser Unit, sondern die Natur der Zustandsvektor-Simulation.
>
> **Die Entropie gibt es nur für einzelne Qubits.** ''QuReducedDensity'' spurt auf eine 2×2-Matrix aus, deren Eigenwerte geschlossen dastehen. Für größere Teilsysteme bräuchte es ein Eigenwertverfahren — der Weg wäre die reelle Einbettung einer hermiteschen Matrix und ''LinEigenSym'' aus [[lyx_-_programmiersprache:units:linalg|std.linalg]]; umgesetzt ist er nicht.
**Noch nicht auf kompensiertes Summieren umgestellt** — drei Stellen. Bei sehr großen Zuständen mit stark verschiedenen Amplituden lohnt eine Gegenprobe über [[lyx_-_programmiersprache:units:sum|std.sum]].
----
Letzte Aktualisierung: 2026-08-16 · alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; der Bell-Zustand über Wahrscheinlichkeiten, Norm und Entropie geprüft, die Entropie-Einheit aus der Quelle abgelesen.