diff --git a/backend/quantinuum/serialize.go b/backend/quantinuum/serialize.go index 29dd7c4..8322e04 100644 --- a/backend/quantinuum/serialize.go +++ b/backend/quantinuum/serialize.go @@ -126,6 +126,20 @@ func qasmGateName(name string) string { return "cswap" case "I": return "id" + case "iSWAP": + return "iswap" + case "ECR": + return "ecr" + case "DCX": + return "dcx" + case "CH": + return "ch" + case "CSX": + return "csx" + case "CCZ": + return "ccz" + case "Sycamore": + return "sycamore" } // For parameterized gates, strip the parameter suffix. if idx := strings.Index(name, "("); idx != -1 { @@ -155,6 +169,10 @@ func qasmGateName(name string) string { return "ryy" case "RZZ": return "rzz" + case "U1": + return "u1" + case "U2": + return "u2" } } return strings.ToLower(name) diff --git a/circuit/builder/builder.go b/circuit/builder/builder.go index 0dba55f..5272788 100644 --- a/circuit/builder/builder.go +++ b/circuit/builder/builder.go @@ -137,11 +137,32 @@ func (b *Builder) SWAP(q0, q1 int) *Builder { return b.Apply(gate.SWAP, q0, q1) } +// ISWAP applies an iSWAP gate. +func (b *Builder) ISWAP(q0, q1 int) *Builder { return b.Apply(gate.ISWAP, q0, q1) } + +// ECR applies an ECR (echoed cross-resonance) gate. +func (b *Builder) ECR(q0, q1 int) *Builder { return b.Apply(gate.ECR, q0, q1) } + +// DCX applies a DCX (double CNOT) gate. +func (b *Builder) DCX(q0, q1 int) *Builder { return b.Apply(gate.DCX, q0, q1) } + +// CH applies a controlled-Hadamard gate. +func (b *Builder) CH(control, target int) *Builder { return b.Apply(gate.CH, control, target) } + +// CSX applies a controlled-√X gate. +func (b *Builder) CSX(control, target int) *Builder { return b.Apply(gate.CSX, control, target) } + +// Sycamore applies Google's Sycamore gate. +func (b *Builder) Sycamore(q0, q1 int) *Builder { return b.Apply(gate.Sycamore, q0, q1) } + // CCX applies a Toffoli (CCX) gate. func (b *Builder) CCX(c0, c1, target int) *Builder { return b.Apply(gate.CCX, c0, c1, target) } +// CCZ applies a doubly-controlled Z gate. +func (b *Builder) CCZ(c0, c1, target int) *Builder { return b.Apply(gate.CCZ, c0, c1, target) } + // MCX applies a multi-controlled X gate. func (b *Builder) MCX(controls []int, target int) *Builder { qubits := make([]int, len(controls)+1) @@ -192,6 +213,27 @@ func (b *Builder) U3(theta, phi, lambda float64, q int) *Builder { return b.Apply(gate.U3(theta, phi, lambda), q) } +// U1 applies a U1 (phase) gate. +func (b *Builder) U1(lambda float64, q int) *Builder { return b.Apply(gate.U1(lambda), q) } + +// U2 applies a U2 gate. +func (b *Builder) U2(phi, lambda float64, q int) *Builder { return b.Apply(gate.U2(phi, lambda), q) } + +// Rot applies a PennyLane-style Rot gate. +func (b *Builder) Rot(phi, theta, omega float64, q int) *Builder { + return b.Apply(gate.Rot(phi, theta, omega), q) +} + +// PhasedXZ applies a Cirq-style PhasedXZ gate. +func (b *Builder) PhasedXZ(x, z, a float64, q int) *Builder { + return b.Apply(gate.PhasedXZ(x, z, a), q) +} + +// GlobalPhase applies a global phase gate. +func (b *Builder) GlobalPhase(phi float64, q int) *Builder { + return b.Apply(gate.GlobalPhase(phi), q) +} + // StatePrep adds a state preparation gate. func (b *Builder) StatePrep(amplitudes []complex128, qubits ...int) *Builder { if b.err != nil { @@ -228,6 +270,16 @@ func (b *Builder) RYY(theta float64, q0, q1 int) *Builder { return b.Apply(gate. // RZZ applies an Ising ZZ rotation gate. func (b *Builder) RZZ(theta float64, q0, q1 int) *Builder { return b.Apply(gate.RZZ(theta), q0, q1) } +// FSim applies a fermionic simulation gate. +func (b *Builder) FSim(theta, phi float64, q0, q1 int) *Builder { + return b.Apply(gate.FSim(theta, phi), q0, q1) +} + +// PSwap applies a parameterized SWAP gate. +func (b *Builder) PSwap(phi float64, q0, q1 int) *Builder { + return b.Apply(gate.PSwap(phi), q0, q1) +} + // SymRX applies a symbolic RX gate. func (b *Builder) SymRX(theta param.Expr, q int) *Builder { return b.Apply(param.SymRX(theta), q) @@ -273,6 +325,36 @@ func (b *Builder) SymRZZ(theta param.Expr, q0, q1 int) *Builder { return b.Apply(param.SymRZZ(theta), q0, q1) } +// SymU1 applies a symbolic U1 gate. +func (b *Builder) SymU1(lambda param.Expr, q int) *Builder { + return b.Apply(param.SymU1(lambda), q) +} + +// SymU2 applies a symbolic U2 gate. +func (b *Builder) SymU2(phi, lambda param.Expr, q int) *Builder { + return b.Apply(param.SymU2(phi, lambda), q) +} + +// SymRot applies a symbolic Rot gate. +func (b *Builder) SymRot(phi, theta, omega param.Expr, q int) *Builder { + return b.Apply(param.SymRot(phi, theta, omega), q) +} + +// SymFSim applies a symbolic FSim gate. +func (b *Builder) SymFSim(theta, phi param.Expr, q0, q1 int) *Builder { + return b.Apply(param.SymFSim(theta, phi), q0, q1) +} + +// SymPSwap applies a symbolic PSwap gate. +func (b *Builder) SymPSwap(phi param.Expr, q0, q1 int) *Builder { + return b.Apply(param.SymPSwap(phi), q0, q1) +} + +// SymGlobalPhase applies a symbolic GlobalPhase gate. +func (b *Builder) SymGlobalPhase(phi param.Expr, q int) *Builder { + return b.Apply(param.SymGlobalPhase(phi), q) +} + // Measure adds a measurement of qubit to classical bit. func (b *Builder) Measure(qubit, clbit int) *Builder { if b.err != nil { diff --git a/circuit/gate/controlled.go b/circuit/gate/controlled.go index 027afa7..435487e 100644 --- a/circuit/gate/controlled.go +++ b/circuit/gate/controlled.go @@ -107,3 +107,9 @@ func (g *controlled) Matrix() []complex128 { }) return g.matrix } + +// C3X returns a 3-controlled X gate (4 qubits total). +func C3X() Gate { return MCX(3) } + +// C4X returns a 4-controlled X gate (5 qubits total). +func C4X() Gate { return MCX(4) } diff --git a/circuit/gate/fixed.go b/circuit/gate/fixed.go index 31110e1..772ae00 100644 --- a/circuit/gate/fixed.go +++ b/circuit/gate/fixed.go @@ -1,6 +1,9 @@ package gate -import "math" +import ( + "math" + "math/cmplx" +) // fixed is a non-parameterized gate with a precomputed matrix. type fixed struct { @@ -17,7 +20,7 @@ func (g *fixed) Params() []float64 { return nil } func (g *fixed) Inverse() Gate { // Self-adjoint gates return themselves. switch g.name { - case "I", "H", "X", "Y", "Z", "CNOT", "CZ", "SWAP", "CCX", "CSWAP": + case "I", "H", "X", "Y", "Z", "CNOT", "CZ", "SWAP", "CCX", "CSWAP", "ECR", "CCZ": return g case "S": return Sdg @@ -129,6 +132,41 @@ var ( 0, 0, 0, -1i, 0, 0, 1i, 0, }} + + ISWAP = &fixed{name: "iSWAP", n: 2, matrix: []complex128{ + 1, 0, 0, 0, + 0, 0, 1i, 0, + 0, 1i, 0, 0, + 0, 0, 0, 1, + }} + + ECR = &fixed{name: "ECR", n: 2, matrix: []complex128{ + 0, 0, complex(s2, 0), complex(0, s2), + 0, 0, complex(0, s2), complex(s2, 0), + complex(s2, 0), complex(0, -s2), 0, 0, + complex(0, -s2), complex(s2, 0), 0, 0, + }} + + DCX = &fixed{name: "DCX", n: 2, matrix: []complex128{ + 1, 0, 0, 0, + 0, 0, 0, 1, + 0, 1, 0, 0, + 0, 0, 1, 0, + }} + + CH = &fixed{name: "CH", n: 2, matrix: []complex128{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, complex(s2, 0), complex(s2, 0), + 0, 0, complex(s2, 0), complex(-s2, 0), + }} + + CSX = &fixed{name: "CSX", n: 2, matrix: []complex128{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, complex(0.5, 0.5), complex(0.5, -0.5), + 0, 0, complex(0.5, -0.5), complex(0.5, 0.5), + }} ) // Standard three-qubit gates. @@ -154,4 +192,29 @@ var ( 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, }} + + CCZ = &fixed{name: "CCZ", n: 3, matrix: []complex128{ + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, -1, + }} +) + +// Special gates. +var ( + Sycamore = &fixed{name: "Sycamore", n: 2, matrix: func() []complex128 { + // FSim(π/2, π/6): Google's native 2-qubit gate. + phi := math.Pi / 6 + return []complex128{ + 1, 0, 0, 0, + 0, 0, -1i, 0, + 0, -1i, 0, 0, + 0, 0, 0, cmplx.Exp(complex(0, -phi)), + } + }()} ) diff --git a/circuit/gate/gate_test.go b/circuit/gate/gate_test.go index 1617ee5..1eb57b0 100644 --- a/circuit/gate/gate_test.go +++ b/circuit/gate/gate_test.go @@ -13,10 +13,15 @@ func TestUnitarity(t *testing.T) { gates := []Gate{ I, H, X, Y, Z, S, Sdg, T, Tdg, SX, CNOT, CZ, SWAP, CY, CCX, CSWAP, + ISWAP, ECR, DCX, CH, CSX, CCZ, Sycamore, RX(math.Pi / 4), RY(math.Pi / 3), RZ(math.Pi / 6), Phase(math.Pi / 4), U3(math.Pi/4, math.Pi/3, math.Pi/6), + U1(math.Pi / 4), U2(math.Pi/4, math.Pi/6), + Rot(math.Pi/4, math.Pi/3, math.Pi/6), + PhasedXZ(0.5, 0.3, 0.1), GlobalPhase(math.Pi / 4), CP(math.Pi / 4), CRZ(math.Pi / 3), CRX(math.Pi / 4), CRY(math.Pi / 5), RXX(math.Pi / 4), RYY(math.Pi / 3), RZZ(math.Pi / 6), + FSim(math.Pi/4, math.Pi/6), PSwap(math.Pi / 4), GPI(math.Pi / 4), GPI2(math.Pi / 3), MS(math.Pi/4, math.Pi/6), } for _, g := range gates { @@ -55,9 +60,14 @@ func assertUnitary(t *testing.T, g Gate) { func TestInverse(t *testing.T) { gates := []Gate{ I, H, X, Y, Z, S, Sdg, T, Tdg, SX, - CNOT, CZ, SWAP, CY, + CNOT, CZ, SWAP, CY, CCX, CSWAP, + ISWAP, ECR, DCX, CH, CSX, CCZ, Sycamore, RX(math.Pi / 4), RY(math.Pi / 3), RZ(math.Pi / 6), + U1(math.Pi / 4), U2(math.Pi/4, math.Pi/6), + Rot(math.Pi/4, math.Pi/3, math.Pi/6), + PhasedXZ(0.5, 0.3, 0.1), GlobalPhase(math.Pi / 4), RXX(math.Pi / 4), RYY(math.Pi / 3), RZZ(math.Pi / 6), + FSim(math.Pi/4, math.Pi/6), PSwap(math.Pi / 4), } for _, g := range gates { t.Run(g.Name(), func(t *testing.T) { @@ -99,6 +109,34 @@ func TestKnownMatrices(t *testing.T) { {"H", H, []complex128{complex(s2, 0), complex(s2, 0), complex(s2, 0), complex(-s2, 0)}}, {"S", S, []complex128{1, 0, 0, 1i}}, {"T", T, []complex128{1, 0, 0, complex(s2, s2)}}, + {"iSWAP", ISWAP, []complex128{ + 1, 0, 0, 0, + 0, 0, 1i, 0, + 0, 1i, 0, 0, + 0, 0, 0, 1, + }}, + {"ECR", ECR, []complex128{ + 0, 0, complex(s2, 0), complex(0, s2), + 0, 0, complex(0, s2), complex(s2, 0), + complex(s2, 0), complex(0, -s2), 0, 0, + complex(0, -s2), complex(s2, 0), 0, 0, + }}, + {"DCX", DCX, []complex128{ + 1, 0, 0, 0, + 0, 0, 0, 1, + 0, 1, 0, 0, + 0, 0, 1, 0, + }}, + {"CCZ[7,7]", CCZ, []complex128{ + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, -1, + }}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { @@ -169,6 +207,48 @@ func TestParameterizedGates(t *testing.T) { 0, 0, 1i, 0, 0, 0, 0, -1i, }) + + // U1(0) = I + m = U1(0).Matrix() + assertMatrixClose(t, "U1(0)", m, []complex128{1, 0, 0, 1}) + + // U1(lambda) should match Phase(lambda) + m = U1(math.Pi / 4).Matrix() + mp := Phase(math.Pi / 4).Matrix() + assertMatrixClose(t, "U1==Phase", m, mp) + + // U2(0,0) = (1/sqrt2) * [[1, -1], [1, 1]] + s2v := 1.0 / math.Sqrt2 + m = U2(0, 0).Matrix() + assertMatrixClose(t, "U2(0,0)", m, []complex128{ + complex(s2v, 0), complex(-s2v, 0), + complex(s2v, 0), complex(s2v, 0), + }) + + // Rot(0,0,0) = I + m = Rot(0, 0, 0).Matrix() + assertMatrixClose(t, "Rot(0,0,0)", m, []complex128{1, 0, 0, 1}) + + // GlobalPhase(0) = I + m = GlobalPhase(0).Matrix() + assertMatrixClose(t, "GlobalPhase(0)", m, []complex128{1, 0, 0, 1}) + + // FSim(0,0) = I + m = FSim(0, 0).Matrix() + assertMatrixClose(t, "FSim(0,0)", m, []complex128{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, + }) + + // FSim(pi/2, pi/6) should match Sycamore matrix + m = FSim(math.Pi/2, math.Pi/6).Matrix() + assertMatrixClose(t, "FSim==Sycamore", m, Sycamore.Matrix()) + + // PSwap(0) = SWAP + m = PSwap(0).Matrix() + assertMatrixClose(t, "PSwap(0)", m, SWAP.Matrix()) } func assertMatrixClose(t *testing.T, name string, got, want []complex128) { @@ -183,6 +263,11 @@ func assertMatrixClose(t *testing.T, name string, got, want []complex128) { } } +// TestCCZMatchesMCZ2 verifies CCZ matrix matches Controlled(Z, 2). +func TestCCZMatchesMCZ2(t *testing.T) { + assertMatrixClose(t, "CCZ==MCZ(2)", CCZ.Matrix(), MCZ(2).Matrix()) +} + func TestGateProperties(t *testing.T) { if H.Name() != "H" { t.Errorf("H.Name() = %q, want %q", H.Name(), "H") diff --git a/circuit/gate/parameterized.go b/circuit/gate/parameterized.go index da00e29..0ad1050 100644 --- a/circuit/gate/parameterized.go +++ b/circuit/gate/parameterized.go @@ -305,3 +305,136 @@ func MS(phi0, phi1 float64) Gate { }, } } + +// U1 returns a phase gate (Qiskit compatibility alias for Phase). +// +// diag(1, exp(iλ)) +func U1(lambda float64) Gate { + return ¶meterized{ + name: fmt.Sprintf("U1(%.4f)", lambda), + n: 1, + params: []float64{lambda}, + matrix: []complex128{ + 1, 0, + 0, cmplx.Exp(complex(0, lambda)), + }, + } +} + +// U2 returns the single-qubit gate U3(π/2, φ, λ). +// +// (1/√2)·[[1, -exp(iλ)], [exp(iφ), exp(i(φ+λ))]] +func U2(phi, lambda float64) Gate { + return ¶meterized{ + name: fmt.Sprintf("U2(%.4f,%.4f)", phi, lambda), + n: 1, + params: []float64{phi, lambda}, + matrix: []complex128{ + complex(s2, 0), + -cmplx.Exp(complex(0, lambda)) * complex(s2, 0), + cmplx.Exp(complex(0, phi)) * complex(s2, 0), + cmplx.Exp(complex(0, phi+lambda)) * complex(s2, 0), + }, + } +} + +// Rot returns the PennyLane-style rotation gate RZ(ω)·RY(θ)·RZ(φ). +func Rot(phi, theta, omega float64) Gate { + // Compute the 2x2 matrix as product: RZ(omega) * RY(theta) * RZ(phi). + cth, sth := math.Cos(theta/2), math.Sin(theta/2) + eipo := cmplx.Exp(complex(0, (phi+omega)/2)) + eimo := cmplx.Exp(complex(0, (phi-omega)/2)) + // RZ(w)*RY(t)*RZ(p) = + // [[ exp(-i(p+w)/2)*cos(t/2), -exp(i(p-w)/2)*sin(t/2)], + // [ exp(-i(p-w)/2)*sin(t/2), exp(i(p+w)/2)*cos(t/2)]] + return ¶meterized{ + name: fmt.Sprintf("Rot(%.4f,%.4f,%.4f)", phi, theta, omega), + n: 1, + params: []float64{phi, theta, omega}, + matrix: []complex128{ + conj(eipo) * complex(cth, 0), + -conj(eimo) * complex(sth, 0), + eimo * complex(sth, 0), + eipo * complex(cth, 0), + }, + } +} + +// PhasedXZ returns the Cirq-style PhasedXZ gate: Z^z · P^a · X^x · (P^a)†. +// Parameters are in half-turns. Z^z = diag(1, e^{iπz}), P^a = diag(1, e^{iπa}), +// X^x has matrix [[cos(πx/2), i·sin(πx/2)], [i·sin(πx/2), cos(πx/2)]]. +func PhasedXZ(xExp, zExp, axisPhaseExp float64) Gate { + cx := math.Cos(math.Pi * xExp / 2) + sx := math.Sin(math.Pi * xExp / 2) + ez := cmplx.Exp(complex(0, math.Pi*zExp)) + ea := cmplx.Exp(complex(0, math.Pi*axisPhaseExp)) + return ¶meterized{ + name: fmt.Sprintf("PhasedXZ(%.4f,%.4f,%.4f)", xExp, zExp, axisPhaseExp), + n: 1, + params: []float64{xExp, zExp, axisPhaseExp}, + matrix: []complex128{ + complex(cx, 0), + conj(ea) * complex(0, sx), + ez * ea * complex(0, sx), + ez * complex(cx, 0), + }, + } +} + +// GlobalPhase returns a gate applying scalar phase e^(iφ) to a single qubit. +// +// e^(iφ)·I = [[e^(iφ), 0], [0, e^(iφ)]] +func GlobalPhase(phi float64) Gate { + ep := cmplx.Exp(complex(0, phi)) + return ¶meterized{ + name: fmt.Sprintf("GlobalPhase(%.4f)", phi), + n: 1, + params: []float64{phi}, + matrix: []complex128{ + ep, 0, + 0, ep, + }, + } +} + +// FSim returns the fermionic simulation gate. +// +// [[1, 0, 0, 0], +// [0, cos θ, -i·sin θ, 0], +// [0, -i·sin θ, cos θ, 0], +// [0, 0, 0, exp(-iφ)]] +func FSim(theta, phi float64) Gate { + ct, st := math.Cos(theta), math.Sin(theta) + return ¶meterized{ + name: fmt.Sprintf("FSim(%.4f,%.4f)", theta, phi), + n: 2, + params: []float64{theta, phi}, + matrix: []complex128{ + 1, 0, 0, 0, + 0, complex(ct, 0), complex(0, -st), 0, + 0, complex(0, -st), complex(ct, 0), 0, + 0, 0, 0, cmplx.Exp(complex(0, -phi)), + }, + } +} + +// PSwap returns the parameterized SWAP gate. +// +// [[1, 0, 0, 0], +// [0, 0, exp(iφ), 0], +// [0, exp(iφ), 0, 0], +// [0, 0, 0, 1]] +func PSwap(phi float64) Gate { + ep := cmplx.Exp(complex(0, phi)) + return ¶meterized{ + name: fmt.Sprintf("PSwap(%.4f)", phi), + n: 2, + params: []float64{phi}, + matrix: []complex128{ + 1, 0, 0, 0, + 0, 0, ep, 0, + 0, ep, 0, 0, + 0, 0, 0, 1, + }, + } +} diff --git a/circuit/param/gate.go b/circuit/param/gate.go index 24cb53f..39bacca 100644 --- a/circuit/param/gate.go +++ b/circuit/param/gate.go @@ -196,3 +196,75 @@ func SymRZZ(theta Expr) gate.Gate { }, } } + +// SymU1 creates a symbolic U1 gate. +func SymU1(lambda Expr) gate.Gate { + return &symbolicGate{ + baseName: "U1", + nQubits: 1, + exprs: []Expr{lambda}, + constructor: func(p []float64) gate.Gate { + return gate.U1(p[0]) + }, + } +} + +// SymU2 creates a symbolic U2 gate. +func SymU2(phi, lambda Expr) gate.Gate { + return &symbolicGate{ + baseName: "U2", + nQubits: 1, + exprs: []Expr{phi, lambda}, + constructor: func(p []float64) gate.Gate { + return gate.U2(p[0], p[1]) + }, + } +} + +// SymRot creates a symbolic Rot gate. +func SymRot(phi, theta, omega Expr) gate.Gate { + return &symbolicGate{ + baseName: "Rot", + nQubits: 1, + exprs: []Expr{phi, theta, omega}, + constructor: func(p []float64) gate.Gate { + return gate.Rot(p[0], p[1], p[2]) + }, + } +} + +// SymFSim creates a symbolic FSim gate. +func SymFSim(theta, phi Expr) gate.Gate { + return &symbolicGate{ + baseName: "FSim", + nQubits: 2, + exprs: []Expr{theta, phi}, + constructor: func(p []float64) gate.Gate { + return gate.FSim(p[0], p[1]) + }, + } +} + +// SymPSwap creates a symbolic PSwap gate. +func SymPSwap(phi Expr) gate.Gate { + return &symbolicGate{ + baseName: "PSwap", + nQubits: 2, + exprs: []Expr{phi}, + constructor: func(p []float64) gate.Gate { + return gate.PSwap(p[0]) + }, + } +} + +// SymGlobalPhase creates a symbolic GlobalPhase gate. +func SymGlobalPhase(phi Expr) gate.Gate { + return &symbolicGate{ + baseName: "GlobalPhase", + nQubits: 1, + exprs: []Expr{phi}, + constructor: func(p []float64) gate.Gate { + return gate.GlobalPhase(p[0]) + }, + } +} diff --git a/qasm/emitter/emitter.go b/qasm/emitter/emitter.go index b28a10b..986a3d1 100644 --- a/qasm/emitter/emitter.go +++ b/qasm/emitter/emitter.go @@ -225,6 +225,20 @@ func qasmGateName(name string) string { return "cswap" case "I": return "id" + case "iSWAP": + return "iswap" + case "ECR": + return "ecr" + case "DCX": + return "dcx" + case "CH": + return "ch" + case "CSX": + return "csx" + case "CCZ": + return "ccz" + case "Sycamore": + return "sycamore" } // For parameterized gates, strip the parameter suffix. if idx := strings.Index(name, "("); idx != -1 { @@ -254,6 +268,20 @@ func qasmGateName(name string) string { return "ryy" case "RZZ": return "rzz" + case "U1": + return "u1" + case "U2": + return "u2" + case "FSim": + return "fsim" + case "PSwap": + return "pswap" + case "Rot": + return "rot" + case "PhasedXZ": + return "phasedxz" + case "GlobalPhase": + return "gphase" } } return strings.ToLower(name) diff --git a/qasm/parser/parser.go b/qasm/parser/parser.go index d7e15b8..4eba45a 100644 --- a/qasm/parser/parser.go +++ b/qasm/parser/parser.go @@ -780,6 +780,20 @@ func (p *parser) resolveGate(name string, params []float64) (gate.Gate, error) { return gate.CCX, nil case "cswap": return gate.CSWAP, nil + case "iswap": + return gate.ISWAP, nil + case "ecr": + return gate.ECR, nil + case "dcx": + return gate.DCX, nil + case "ch": + return gate.CH, nil + case "csx": + return gate.CSX, nil + case "ccz": + return gate.CCZ, nil + case "sycamore": + return gate.Sycamore, nil // Parameterized gates. case "rx": @@ -811,7 +825,12 @@ func (p *parser) resolveGate(name string, params []float64) (gate.Gate, error) { if len(params) != 1 { return nil, fmt.Errorf("u1 requires 1 parameter, got %d", len(params)) } - return gate.Phase(params[0]), nil + return gate.U1(params[0]), nil + case "u2": + if len(params) != 2 { + return nil, fmt.Errorf("u2 requires 2 parameters, got %d", len(params)) + } + return gate.U2(params[0], params[1]), nil case "cp", "cphase": if len(params) != 1 { return nil, fmt.Errorf("cp requires 1 parameter, got %d", len(params)) @@ -848,8 +867,10 @@ func (p *parser) resolveGate(name string, params []float64) (gate.Gate, error) { } return gate.RZZ(params[0]), nil case "gphase": - // Global phase — no qubits, just a parameter. Treat as identity for IR. - return gate.I, nil + if len(params) != 1 { + return nil, fmt.Errorf("gphase requires 1 parameter, got %d", len(params)) + } + return gate.GlobalPhase(params[0]), nil } // Check user-defined gates. diff --git a/quil/emitter/emitter.go b/quil/emitter/emitter.go index 7dc2517..3ceba60 100644 --- a/quil/emitter/emitter.go +++ b/quil/emitter/emitter.go @@ -189,6 +189,10 @@ func quilGate(name string, params []float64) (string, []float64, error) { return "CCNOT", nil, nil case "CSWAP": return "CSWAP", nil, nil + case "iSWAP": + return "ISWAP", nil, nil + case "CCZ": + return "CONTROLLED CONTROLLED Z", nil, nil } // Parameterized gates: strip the "(..." suffix from the name. diff --git a/sim/statevector/kernel2q.go b/sim/statevector/kernel2q.go index 2d9719b..269f755 100644 --- a/sim/statevector/kernel2q.go +++ b/sim/statevector/kernel2q.go @@ -41,6 +41,20 @@ func (s *Sim) dispatchGate2(g gate.Gate, q0, q1 int) { s.kernel2qCY(q0, q1) } return + case gate.ISWAP: + if parallel { + s.kernel2qISWAPParallel(q0, q1) + } else { + s.kernel2qISWAP(q0, q1) + } + return + case gate.Sycamore: + if parallel { + s.kernel2qSycamoreParallel(q0, q1) + } else { + s.kernel2qSycamore(q0, q1) + } + return } // Name-based dispatch for parameterized gates. @@ -246,6 +260,49 @@ func (s *Sim) kernel2qGeneric(q0, q1 int, m []complex128) { } } +// kernel2qISWAP: iSWAP swaps |01⟩↔|10⟩ with factor i. +func (s *Sim) kernel2qISWAP(q0, q1 int) { + mask0, mask1, lo, hi := blockStride2(q0, q1) + n := len(s.state) + loMask := 1 << lo + hiMask := 1 << hi + for hi0 := 0; hi0 < n; hi0 += hiMask << 1 { + for lo0 := hi0; lo0 < hi0+hiMask; lo0 += loMask << 1 { + for offset := lo0; offset < lo0+loMask; offset++ { + i01 := offset | mask1 + i10 := offset | mask0 + a1, a2 := s.state[i01], s.state[i10] + s.state[i01] = 1i * a2 + s.state[i10] = 1i * a1 + } + } + } +} + +// kernel2qSycamore: FSim(π/2, π/6) — iSWAP on |01⟩↔|10⟩ plus phase on |11⟩. +func (s *Sim) kernel2qSycamore(q0, q1 int) { + mask0, mask1, lo, hi := blockStride2(q0, q1) + n := len(s.state) + loMask := 1 << lo + hiMask := 1 << hi + // Precompute exp(-iπ/6) for the |11⟩ phase. + m := gate.Sycamore.Matrix() + d11 := m[15] + for hi0 := 0; hi0 < n; hi0 += hiMask << 1 { + for lo0 := hi0; lo0 < hi0+hiMask; lo0 += loMask << 1 { + for offset := lo0; offset < lo0+loMask; offset++ { + i01 := offset | mask1 + i10 := offset | mask0 + i11 := offset | mask0 | mask1 + a1, a2 := s.state[i01], s.state[i10] + s.state[i01] = -1i * a2 + s.state[i10] = -1i * a1 + s.state[i11] *= d11 + } + } + } +} + // --- Parallel kernels --- // parallelBlocks2 computes worker distribution for 2Q parallel kernels. @@ -537,3 +594,77 @@ func (s *Sim) kernel2qGenericParallel(q0, q1 int, m []complex128) { } wg.Wait() } + +func (s *Sim) kernel2qISWAPParallel(q0, q1 int) { + mask0, mask1, lo, hi := blockStride2(q0, q1) + loMask := 1 << lo + hiMask := 1 << hi + hiStep := hiMask << 1 + nBlocks, nWorkers := s.parallelBlocks2(hiMask) + blocksPerWorker := nBlocks / nWorkers + + var wg sync.WaitGroup + wg.Add(nWorkers) + for w := range nWorkers { + startBlock := w * blocksPerWorker + endBlock := startBlock + blocksPerWorker + if w == nWorkers-1 { + endBlock = nBlocks + } + go func(sb, eb int) { + defer wg.Done() + for b := sb; b < eb; b++ { + hi0 := b * hiStep + for lo0 := hi0; lo0 < hi0+hiMask; lo0 += loMask << 1 { + for offset := lo0; offset < lo0+loMask; offset++ { + i01 := offset | mask1 + i10 := offset | mask0 + a1, a2 := s.state[i01], s.state[i10] + s.state[i01] = 1i * a2 + s.state[i10] = 1i * a1 + } + } + } + }(startBlock, endBlock) + } + wg.Wait() +} + +func (s *Sim) kernel2qSycamoreParallel(q0, q1 int) { + mask0, mask1, lo, hi := blockStride2(q0, q1) + loMask := 1 << lo + hiMask := 1 << hi + hiStep := hiMask << 1 + nBlocks, nWorkers := s.parallelBlocks2(hiMask) + blocksPerWorker := nBlocks / nWorkers + sm := gate.Sycamore.Matrix() + d11 := sm[15] + + var wg sync.WaitGroup + wg.Add(nWorkers) + for w := range nWorkers { + startBlock := w * blocksPerWorker + endBlock := startBlock + blocksPerWorker + if w == nWorkers-1 { + endBlock = nBlocks + } + go func(sb, eb int) { + defer wg.Done() + for b := sb; b < eb; b++ { + hi0 := b * hiStep + for lo0 := hi0; lo0 < hi0+hiMask; lo0 += loMask << 1 { + for offset := lo0; offset < lo0+loMask; offset++ { + i01 := offset | mask1 + i10 := offset | mask0 + i11 := offset | mask0 | mask1 + a1, a2 := s.state[i01], s.state[i10] + s.state[i01] = -1i * a2 + s.state[i10] = -1i * a1 + s.state[i11] *= d11 + } + } + } + }(startBlock, endBlock) + } + wg.Wait() +} diff --git a/sim/statevector/kernel3q.go b/sim/statevector/kernel3q.go index c47c4c9..adfaa5a 100644 --- a/sim/statevector/kernel3q.go +++ b/sim/statevector/kernel3q.go @@ -25,6 +25,13 @@ func (s *Sim) dispatchGate3(g gate.Gate, q0, q1, q2 int) { s.kernel3qCSWAP(q0, q1, q2) } return + case gate.CCZ: + if parallel { + s.kernel3qCCZParallel(q0, q1, q2) + } else { + s.kernel3qCCZ(q0, q1, q2) + } + return } // Generic fallback. @@ -102,6 +109,25 @@ func (s *Sim) kernel3qCSWAP(q0, q1, q2 int) { } } +// kernel3qCCZ: CCZ negates |111> amplitude (all controls set). +func (s *Sim) kernel3qCCZ(q0, q1, q2 int) { + mask0, mask1, mask2, lo, mid, hi := blockStride3(q0, q1, q2) + n := len(s.state) + loMask := 1 << lo + midMask := 1 << mid + hiMask := 1 << hi + for hi0 := 0; hi0 < n; hi0 += hiMask << 1 { + for mid0 := hi0; mid0 < hi0+hiMask; mid0 += midMask << 1 { + for lo0 := mid0; lo0 < mid0+midMask; lo0 += loMask << 1 { + for offset := lo0; offset < lo0+loMask; offset++ { + i111 := offset | mask0 | mask1 | mask2 + s.state[i111] = -s.state[i111] + } + } + } + } +} + // kernel3qGeneric handles arbitrary 3Q gates with full 8×8 matmul. func (s *Sim) kernel3qGeneric(q0, q1, q2 int, m []complex128) { mask0, mask1, mask2, lo, mid, hi := blockStride3(q0, q1, q2) @@ -232,6 +258,41 @@ func (s *Sim) kernel3qCSWAPParallel(q0, q1, q2 int) { wg.Wait() } +func (s *Sim) kernel3qCCZParallel(q0, q1, q2 int) { + mask0, mask1, mask2, lo, mid, hi := blockStride3(q0, q1, q2) + loMask := 1 << lo + midMask := 1 << mid + hiMask := 1 << hi + hiStep := hiMask << 1 + nBlocks, nWorkers := s.parallelBlocks3(hiMask) + blocksPerWorker := nBlocks / nWorkers + + var wg sync.WaitGroup + wg.Add(nWorkers) + for w := range nWorkers { + startBlock := w * blocksPerWorker + endBlock := startBlock + blocksPerWorker + if w == nWorkers-1 { + endBlock = nBlocks + } + go func(sb, eb int) { + defer wg.Done() + for b := sb; b < eb; b++ { + hi0 := b * hiStep + for mid0 := hi0; mid0 < hi0+hiMask; mid0 += midMask << 1 { + for lo0 := mid0; lo0 < mid0+midMask; lo0 += loMask << 1 { + for offset := lo0; offset < lo0+loMask; offset++ { + i111 := offset | mask0 | mask1 | mask2 + s.state[i111] = -s.state[i111] + } + } + } + } + }(startBlock, endBlock) + } + wg.Wait() +} + func (s *Sim) kernel3qGenericParallel(q0, q1, q2 int, m []complex128) { mask0, mask1, mask2, lo, mid, hi := blockStride3(q0, q1, q2) loMask := 1 << lo