UNPKG

@qbead/bloch-sphere

Version:

A 3D Bloch Sphere visualisation built with Three.js and TypeScript.

490 lines (453 loc) 17.8 kB
import { describe, expect, test } from 'bun:test' import * as gates from './gates' import { Complex } from './complex' describe('Quantum Gates', () => { describe('identity()', () => { test('returns identity matrix', () => { const id = gates.identity() expect(id.a.real).toBe(1) expect(id.a.imag).toBe(0) expect(id.b.real).toBe(0) expect(id.b.imag).toBe(0) expect(id.c.real).toBe(0) expect(id.c.imag).toBe(0) expect(id.d.real).toBe(1) expect(id.d.imag).toBe(0) }) test('identity squared is identity', () => { const id = gates.identity() const id2 = id.times(id) expect(id2.a.real).toBeCloseTo(1, 10) expect(id2.a.imag).toBeCloseTo(0, 10) expect(id2.b.real).toBeCloseTo(0, 10) expect(id2.b.imag).toBeCloseTo(0, 10) expect(id2.c.real).toBeCloseTo(0, 10) expect(id2.c.imag).toBeCloseTo(0, 10) expect(id2.d.real).toBeCloseTo(1, 10) expect(id2.d.imag).toBeCloseTo(0, 10) }) }) describe('Pauli X gate', () => { test('x() returns correct matrix [[0, 1], [1, 0]]', () => { const X = gates.x() expect(X.a.real).toBe(0) expect(X.a.imag).toBe(0) expect(X.b.real).toBe(1) expect(X.b.imag).toBe(0) expect(X.c.real).toBe(1) expect(X.c.imag).toBe(0) expect(X.d.real).toBe(0) expect(X.d.imag).toBe(0) }) test('X² = I (X squared is identity)', () => { const X = gates.x() const X2 = X.times(X) expect(X2.a.real).toBeCloseTo(1, 10) expect(X2.a.imag).toBeCloseTo(0, 10) expect(X2.b.real).toBeCloseTo(0, 10) expect(X2.b.imag).toBeCloseTo(0, 10) expect(X2.c.real).toBeCloseTo(0, 10) expect(X2.c.imag).toBeCloseTo(0, 10) expect(X2.d.real).toBeCloseTo(1, 10) expect(X2.d.imag).toBeCloseTo(0, 10) }) test('X is Hermitian (X† = X)', () => { const X = gates.x() const Xdag = X.conjugateTranspose() expect(Xdag.a.real).toBeCloseTo(X.a.real, 10) expect(Xdag.a.imag).toBeCloseTo(X.a.imag, 10) expect(Xdag.b.real).toBeCloseTo(X.b.real, 10) expect(Xdag.b.imag).toBeCloseTo(X.b.imag, 10) expect(Xdag.c.real).toBeCloseTo(X.c.real, 10) expect(Xdag.c.imag).toBeCloseTo(X.c.imag, 10) expect(Xdag.d.real).toBeCloseTo(X.d.real, 10) expect(Xdag.d.imag).toBeCloseTo(X.d.imag, 10) }) test('X is unitary (X†X = I)', () => { const X = gates.x() const XdagX = X.conjugateTranspose().times(X) expect(XdagX.a.real).toBeCloseTo(1, 10) expect(XdagX.a.imag).toBeCloseTo(0, 10) expect(XdagX.b.real).toBeCloseTo(0, 10) expect(XdagX.b.imag).toBeCloseTo(0, 10) expect(XdagX.c.real).toBeCloseTo(0, 10) expect(XdagX.c.imag).toBeCloseTo(0, 10) expect(XdagX.d.real).toBeCloseTo(1, 10) expect(XdagX.d.imag).toBeCloseTo(0, 10) }) test('not() is alias for x()', () => { const X = gates.x() const NOT = gates.not() expect(NOT.a.real).toBe(X.a.real) expect(NOT.a.imag).toBe(X.a.imag) expect(NOT.b.real).toBe(X.b.real) expect(NOT.b.imag).toBe(X.b.imag) expect(NOT.c.real).toBe(X.c.real) expect(NOT.c.imag).toBe(X.c.imag) expect(NOT.d.real).toBe(X.d.real) expect(NOT.d.imag).toBe(X.d.imag) }) }) describe('Pauli Y gate', () => { test('y() returns correct matrix [[0, -i], [i, 0]]', () => { const Y = gates.y() expect(Math.abs(Y.a.real)).toBe(0) expect(Math.abs(Y.a.imag)).toBe(0) expect(Math.abs(Y.b.real)).toBe(0) expect(Y.b.imag).toBe(-1) expect(Math.abs(Y.c.real)).toBe(0) expect(Y.c.imag).toBe(1) expect(Math.abs(Y.d.real)).toBe(0) expect(Math.abs(Y.d.imag)).toBe(0) }) test('Y² = I (Y squared is identity)', () => { const Y = gates.y() const Y2 = Y.times(Y) expect(Y2.a.real).toBeCloseTo(1, 10) expect(Y2.a.imag).toBeCloseTo(0, 10) expect(Y2.b.real).toBeCloseTo(0, 10) expect(Y2.b.imag).toBeCloseTo(0, 10) expect(Y2.c.real).toBeCloseTo(0, 10) expect(Y2.c.imag).toBeCloseTo(0, 10) expect(Y2.d.real).toBeCloseTo(1, 10) expect(Y2.d.imag).toBeCloseTo(0, 10) }) test('Y is Hermitian (Y† = Y)', () => { const Y = gates.y() const Ydag = Y.conjugateTranspose() expect(Ydag.a.real).toBeCloseTo(Y.a.real, 10) expect(Ydag.a.imag).toBeCloseTo(Y.a.imag, 10) expect(Ydag.b.real).toBeCloseTo(Y.b.real, 10) expect(Ydag.b.imag).toBeCloseTo(Y.b.imag, 10) expect(Ydag.c.real).toBeCloseTo(Y.c.real, 10) expect(Ydag.c.imag).toBeCloseTo(Y.c.imag, 10) expect(Ydag.d.real).toBeCloseTo(Y.d.real, 10) expect(Ydag.d.imag).toBeCloseTo(Y.d.imag, 10) }) test('Y is unitary (Y†Y = I)', () => { const Y = gates.y() const YdagY = Y.conjugateTranspose().times(Y) expect(YdagY.a.real).toBeCloseTo(1, 10) expect(YdagY.a.imag).toBeCloseTo(0, 10) expect(YdagY.b.real).toBeCloseTo(0, 10) expect(YdagY.b.imag).toBeCloseTo(0, 10) expect(YdagY.c.real).toBeCloseTo(0, 10) expect(YdagY.c.imag).toBeCloseTo(0, 10) expect(YdagY.d.real).toBeCloseTo(1, 10) expect(YdagY.d.imag).toBeCloseTo(0, 10) }) }) describe('Pauli Z gate', () => { test('z() returns correct matrix [[1, 0], [0, -1]]', () => { const Z = gates.z() expect(Z.a.real).toBe(1) expect(Z.a.imag).toBe(0) expect(Z.b.real).toBe(0) expect(Z.b.imag).toBe(0) expect(Z.c.real).toBe(0) expect(Z.c.imag).toBe(0) expect(Z.d.real).toBe(-1) expect(Z.d.imag).toBe(0) }) test('Z² = I (Z squared is identity)', () => { const Z = gates.z() const Z2 = Z.times(Z) expect(Z2.a.real).toBeCloseTo(1, 10) expect(Z2.a.imag).toBeCloseTo(0, 10) expect(Z2.b.real).toBeCloseTo(0, 10) expect(Z2.b.imag).toBeCloseTo(0, 10) expect(Z2.c.real).toBeCloseTo(0, 10) expect(Z2.c.imag).toBeCloseTo(0, 10) expect(Z2.d.real).toBeCloseTo(1, 10) expect(Z2.d.imag).toBeCloseTo(0, 10) }) test('Z is Hermitian (Z† = Z)', () => { const Z = gates.z() const Zdag = Z.conjugateTranspose() expect(Zdag.a.real).toBeCloseTo(Z.a.real, 10) expect(Zdag.a.imag).toBeCloseTo(Z.a.imag, 10) expect(Zdag.b.real).toBeCloseTo(Z.b.real, 10) expect(Zdag.b.imag).toBeCloseTo(Z.b.imag, 10) expect(Zdag.c.real).toBeCloseTo(Z.c.real, 10) expect(Zdag.c.imag).toBeCloseTo(Z.c.imag, 10) expect(Zdag.d.real).toBeCloseTo(Z.d.real, 10) expect(Zdag.d.imag).toBeCloseTo(Z.d.imag, 10) }) test('Z is unitary (Z†Z = I)', () => { const Z = gates.z() const ZdagZ = Z.conjugateTranspose().times(Z) expect(ZdagZ.a.real).toBeCloseTo(1, 10) expect(ZdagZ.a.imag).toBeCloseTo(0, 10) expect(ZdagZ.b.real).toBeCloseTo(0, 10) expect(ZdagZ.b.imag).toBeCloseTo(0, 10) expect(ZdagZ.c.real).toBeCloseTo(0, 10) expect(ZdagZ.c.imag).toBeCloseTo(0, 10) expect(ZdagZ.d.real).toBeCloseTo(1, 10) expect(ZdagZ.d.imag).toBeCloseTo(0, 10) }) }) describe('Hadamard gate', () => { test('hadamard() returns matrix (1/√2)[[1, 1], [1, -1]]', () => { const H = gates.hadamard() const sqrt2 = 1 / Math.sqrt(2) expect(H.a.real).toBeCloseTo(sqrt2, 10) expect(H.a.imag).toBeCloseTo(0, 10) expect(H.b.real).toBeCloseTo(sqrt2, 10) expect(H.b.imag).toBeCloseTo(0, 10) expect(H.c.real).toBeCloseTo(sqrt2, 10) expect(H.c.imag).toBeCloseTo(0, 10) expect(H.d.real).toBeCloseTo(-sqrt2, 10) expect(H.d.imag).toBeCloseTo(0, 10) }) test('H² = I (Hadamard squared is identity)', () => { const H = gates.hadamard() const H2 = H.times(H) expect(H2.a.real).toBeCloseTo(1, 10) expect(H2.a.imag).toBeCloseTo(0, 10) expect(H2.b.real).toBeCloseTo(0, 10) expect(H2.b.imag).toBeCloseTo(0, 10) expect(H2.c.real).toBeCloseTo(0, 10) expect(H2.c.imag).toBeCloseTo(0, 10) expect(H2.d.real).toBeCloseTo(1, 10) expect(H2.d.imag).toBeCloseTo(0, 10) }) test('H is Hermitian (H† = H)', () => { const H = gates.hadamard() const Hdag = H.conjugateTranspose() expect(Hdag.a.real).toBeCloseTo(H.a.real, 10) expect(Hdag.a.imag).toBeCloseTo(H.a.imag, 10) expect(Hdag.b.real).toBeCloseTo(H.b.real, 10) expect(Hdag.b.imag).toBeCloseTo(H.b.imag, 10) expect(Hdag.c.real).toBeCloseTo(H.c.real, 10) expect(Hdag.c.imag).toBeCloseTo(H.c.imag, 10) expect(Hdag.d.real).toBeCloseTo(H.d.real, 10) expect(Hdag.d.imag).toBeCloseTo(H.d.imag, 10) }) test('H is unitary (H†H = I)', () => { const H = gates.hadamard() const HdagH = H.conjugateTranspose().times(H) expect(HdagH.a.real).toBeCloseTo(1, 10) expect(HdagH.a.imag).toBeCloseTo(0, 10) expect(HdagH.b.real).toBeCloseTo(0, 10) expect(HdagH.b.imag).toBeCloseTo(0, 10) expect(HdagH.c.real).toBeCloseTo(0, 10) expect(HdagH.c.imag).toBeCloseTo(0, 10) expect(HdagH.d.real).toBeCloseTo(1, 10) expect(HdagH.d.imag).toBeCloseTo(0, 10) }) }) describe('phase() gate', () => { test('phase(0) is identity', () => { const P = gates.phase(0) expect(P.a.real).toBeCloseTo(1, 10) expect(P.a.imag).toBeCloseTo(0, 10) expect(P.b.real).toBeCloseTo(0, 10) expect(P.b.imag).toBeCloseTo(0, 10) expect(P.c.real).toBeCloseTo(0, 10) expect(P.c.imag).toBeCloseTo(0, 10) expect(P.d.real).toBeCloseTo(1, 10) expect(P.d.imag).toBeCloseTo(0, 10) }) test('phase(π) is Z gate', () => { const P = gates.phase(Math.PI) const Z = gates.z() expect(P.a.real).toBeCloseTo(Z.a.real, 10) expect(P.a.imag).toBeCloseTo(Z.a.imag, 10) expect(P.b.real).toBeCloseTo(Z.b.real, 10) expect(P.b.imag).toBeCloseTo(Z.b.imag, 10) expect(P.c.real).toBeCloseTo(Z.c.real, 10) expect(P.c.imag).toBeCloseTo(Z.c.imag, 10) expect(P.d.real).toBeCloseTo(Z.d.real, 10) expect(P.d.imag).toBeCloseTo(Z.d.imag, 10) }) test('phase(π/2) is S gate', () => { const S = gates.phase(Math.PI / 2) expect(S.a.real).toBeCloseTo(1, 10) expect(S.a.imag).toBeCloseTo(0, 10) expect(S.b.real).toBeCloseTo(0, 10) expect(S.b.imag).toBeCloseTo(0, 10) expect(S.c.real).toBeCloseTo(0, 10) expect(S.c.imag).toBeCloseTo(0, 10) expect(S.d.real).toBeCloseTo(0, 10) expect(S.d.imag).toBeCloseTo(1, 10) }) test('phase gate is unitary', () => { const P = gates.phase(Math.PI / 3) const PdagP = P.conjugateTranspose().times(P) expect(PdagP.a.real).toBeCloseTo(1, 10) expect(PdagP.a.imag).toBeCloseTo(0, 10) expect(PdagP.b.real).toBeCloseTo(0, 10) expect(PdagP.b.imag).toBeCloseTo(0, 10) expect(PdagP.c.real).toBeCloseTo(0, 10) expect(PdagP.c.imag).toBeCloseTo(0, 10) expect(PdagP.d.real).toBeCloseTo(1, 10) expect(PdagP.d.imag).toBeCloseTo(0, 10) }) }) describe('Rotation gates', () => { describe('rx() - rotation around X', () => { test('rx(0) is identity', () => { const RX = gates.rx(0) expect(RX.a.real).toBeCloseTo(1, 10) expect(RX.a.imag).toBeCloseTo(0, 10) expect(RX.b.real).toBeCloseTo(0, 10) expect(RX.b.imag).toBeCloseTo(0, 10) expect(RX.c.real).toBeCloseTo(0, 10) expect(RX.c.imag).toBeCloseTo(0, 10) expect(RX.d.real).toBeCloseTo(1, 10) expect(RX.d.imag).toBeCloseTo(0, 10) }) test('rx(π) is -iX', () => { const RX = gates.rx(Math.PI) // rx(π) = [[0, ±i], [±i, 0]] - sign depends on convention expect(Math.abs(RX.a.real)).toBeCloseTo(0, 10) expect(Math.abs(RX.a.imag)).toBeCloseTo(0, 10) expect(Math.abs(RX.b.real)).toBeCloseTo(0, 10) expect(Math.abs(RX.b.imag)).toBeCloseTo(1, 10) expect(Math.abs(RX.c.real)).toBeCloseTo(0, 10) expect(Math.abs(RX.c.imag)).toBeCloseTo(1, 10) expect(Math.abs(RX.d.real)).toBeCloseTo(0, 10) expect(Math.abs(RX.d.imag)).toBeCloseTo(0, 10) }) test('rx is unitary', () => { const RX = gates.rx(Math.PI / 4) const RXdagRX = RX.conjugateTranspose().times(RX) expect(RXdagRX.a.real).toBeCloseTo(1, 10) expect(RXdagRX.a.imag).toBeCloseTo(0, 10) expect(RXdagRX.b.real).toBeCloseTo(0, 10) expect(RXdagRX.b.imag).toBeCloseTo(0, 10) expect(RXdagRX.c.real).toBeCloseTo(0, 10) expect(RXdagRX.c.imag).toBeCloseTo(0, 10) expect(RXdagRX.d.real).toBeCloseTo(1, 10) expect(RXdagRX.d.imag).toBeCloseTo(0, 10) }) }) describe('ry() - rotation around Y', () => { test('ry(0) is identity', () => { const RY = gates.ry(0) expect(RY.a.real).toBeCloseTo(1, 10) expect(RY.a.imag).toBeCloseTo(0, 10) expect(RY.b.real).toBeCloseTo(0, 10) expect(RY.b.imag).toBeCloseTo(0, 10) expect(RY.c.real).toBeCloseTo(0, 10) expect(RY.c.imag).toBeCloseTo(0, 10) expect(RY.d.real).toBeCloseTo(1, 10) expect(RY.d.imag).toBeCloseTo(0, 10) }) test('ry(π/2) creates superposition', () => { const RY = gates.ry(Math.PI / 2) const sqrt2 = 1 / Math.sqrt(2) expect(RY.a.real).toBeCloseTo(sqrt2, 10) expect(RY.a.imag).toBeCloseTo(0, 10) expect(RY.b.real).toBeCloseTo(-sqrt2, 10) expect(RY.b.imag).toBeCloseTo(0, 10) expect(RY.c.real).toBeCloseTo(sqrt2, 10) expect(RY.c.imag).toBeCloseTo(0, 10) expect(RY.d.real).toBeCloseTo(sqrt2, 10) expect(RY.d.imag).toBeCloseTo(0, 10) }) test('ry is unitary', () => { const RY = gates.ry(Math.PI / 3) const RYdagRY = RY.conjugateTranspose().times(RY) expect(RYdagRY.a.real).toBeCloseTo(1, 10) expect(RYdagRY.a.imag).toBeCloseTo(0, 10) expect(RYdagRY.b.real).toBeCloseTo(0, 10) expect(RYdagRY.b.imag).toBeCloseTo(0, 10) expect(RYdagRY.c.real).toBeCloseTo(0, 10) expect(RYdagRY.c.imag).toBeCloseTo(0, 10) expect(RYdagRY.d.real).toBeCloseTo(1, 10) expect(RYdagRY.d.imag).toBeCloseTo(0, 10) }) }) describe('rz() - rotation around Z', () => { test('rz(0) is identity', () => { const RZ = gates.rz(0) expect(RZ.a.real).toBeCloseTo(1, 10) expect(RZ.a.imag).toBeCloseTo(0, 10) expect(RZ.b.real).toBeCloseTo(0, 10) expect(RZ.b.imag).toBeCloseTo(0, 10) expect(RZ.c.real).toBeCloseTo(0, 10) expect(RZ.c.imag).toBeCloseTo(0, 10) expect(RZ.d.real).toBeCloseTo(1, 10) expect(RZ.d.imag).toBeCloseTo(0, 10) }) test('rz is unitary', () => { const RZ = gates.rz(Math.PI / 6) const RZdagRZ = RZ.conjugateTranspose().times(RZ) expect(RZdagRZ.a.real).toBeCloseTo(1, 10) expect(RZdagRZ.a.imag).toBeCloseTo(0, 10) expect(RZdagRZ.b.real).toBeCloseTo(0, 10) expect(RZdagRZ.b.imag).toBeCloseTo(0, 10) expect(RZdagRZ.c.real).toBeCloseTo(0, 10) expect(RZdagRZ.c.imag).toBeCloseTo(0, 10) expect(RZdagRZ.d.real).toBeCloseTo(1, 10) expect(RZdagRZ.d.imag).toBeCloseTo(0, 10) }) }) }) describe('Pauli matrices anticommutation', () => { test('XY + YX = 0', () => { const X = gates.x() const Y = gates.y() const XY = X.times(Y) const YX = Y.times(X) const sum = XY.plus(YX) expect(sum.a.magnitude).toBeCloseTo(0, 10) expect(sum.b.magnitude).toBeCloseTo(0, 10) expect(sum.c.magnitude).toBeCloseTo(0, 10) expect(sum.d.magnitude).toBeCloseTo(0, 10) }) test('XZ + ZX = 0', () => { const X = gates.x() const Z = gates.z() const XZ = X.times(Z) const ZX = Z.times(X) const sum = XZ.plus(ZX) expect(sum.a.magnitude).toBeCloseTo(0, 10) expect(sum.b.magnitude).toBeCloseTo(0, 10) expect(sum.c.magnitude).toBeCloseTo(0, 10) expect(sum.d.magnitude).toBeCloseTo(0, 10) }) test('YZ + ZY = 0', () => { const Y = gates.y() const Z = gates.z() const YZ = Y.times(Z) const ZY = Z.times(Y) const sum = YZ.plus(ZY) expect(sum.a.magnitude).toBeCloseTo(0, 10) expect(sum.b.magnitude).toBeCloseTo(0, 10) expect(sum.c.magnitude).toBeCloseTo(0, 10) expect(sum.d.magnitude).toBeCloseTo(0, 10) }) }) describe('Special gate relations', () => { test('HXH = Z', () => { const H = gates.hadamard() const X = gates.x() const Z = gates.z() const HXH = H.times(X).times(H) expect(HXH.a.real).toBeCloseTo(Z.a.real, 10) expect(HXH.a.imag).toBeCloseTo(Z.a.imag, 10) expect(HXH.b.real).toBeCloseTo(Z.b.real, 10) expect(HXH.b.imag).toBeCloseTo(Z.b.imag, 10) expect(HXH.c.real).toBeCloseTo(Z.c.real, 10) expect(HXH.c.imag).toBeCloseTo(Z.c.imag, 10) expect(HXH.d.real).toBeCloseTo(Z.d.real, 10) expect(HXH.d.imag).toBeCloseTo(Z.d.imag, 10) }) test('HZH = X', () => { const H = gates.hadamard() const X = gates.x() const Z = gates.z() const HZH = H.times(Z).times(H) expect(HZH.a.real).toBeCloseTo(X.a.real, 10) expect(HZH.a.imag).toBeCloseTo(X.a.imag, 10) expect(HZH.b.real).toBeCloseTo(X.b.real, 10) expect(HZH.b.imag).toBeCloseTo(X.b.imag, 10) expect(HZH.c.real).toBeCloseTo(X.c.real, 10) expect(HZH.c.imag).toBeCloseTo(X.c.imag, 10) expect(HZH.d.real).toBeCloseTo(X.d.real, 10) expect(HZH.d.imag).toBeCloseTo(X.d.imag, 10) }) }) })