Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions backend/quantinuum/serialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
82 changes: 82 additions & 0 deletions circuit/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions circuit/gate/controlled.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
67 changes: 65 additions & 2 deletions circuit/gate/fixed.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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)),
}
}()}
)
87 changes: 86 additions & 1 deletion circuit/gate/gate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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")
Expand Down
Loading
Loading