std.tensor — komplexe Tensoren und Quantenzustände

Tensoren mit allgemeiner Kontraktion über beliebige Achsen; darauf aufgesetzt Quantenzustände, Gatter, reduzierte Dichtematrix und Verschränkungsentropie.

Mathematik-Units · std.complex · std.linalg · 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 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 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.