UNPKG

@qbead/bloch-sphere

Version:

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

212 lines (194 loc) 5.93 kB
import { Complex } from './complex' import { Vector3 } from 'three' import { Operator } from './operator' import { lerpAngle } from './interpolation' import { normalizeAzimuthal } from './angles' /** * A class representing a Bloch vector * * Angle `theta` is the angle between the Bloch vector and the z-axis * Angle `phi` is the "timezone" angle, the angle from the x-axis in the xy-plane * * This class extends the Vector3 class from three.js and provides additional * functionality. * * @example * ```ts * const blochVector = BlochVector.from(1, 0, 0) * const blochVector2 = BlochVector.from([0, 1, 0]) * const blochVector3 = BlochVector.fromAngles(0.5 * Math.PI, 1.5 * Math.PI) * const blochVector4 = BlochVector.MINUS_I * const blochVector5 = BlochVector.random() * ``` */ export class BlochVector extends Vector3 { /** * A bloch vector representing the zero state */ static get ZERO() { return new BlochVector(0, 0, 1) } /** * A bloch vector representing the one state */ static get ONE() { return new BlochVector(0, 0, -1) } /** * A bloch vector representing the plus state (|+>) or (|0> + |1>)/sqrt(2) */ static get PLUS() { return new BlochVector(1, 0, 0) } /** * A bloch vector representing the minus state (|->) or (|0> - |1>)/sqrt(2) */ static get MINUS() { return new BlochVector(-1, 0, 0) } /** * A bloch vector representing the imaginary state (|i>) or (|0> + i|1>)/sqrt(2) */ static get I() { return new BlochVector(0, 1, 0) } /** * A bloch vector representing the minus imaginary state (|-i>) or (|0> - i|1>)/sqrt(2) */ static get MINUS_I() { return new BlochVector(0, -1, 0) } /** * Generate a random Bloch vector with magnitude 1 */ static random() { const theta = Math.random() * Math.PI const phi = Math.random() * 2 * Math.PI return BlochVector.fromAngles(theta, phi) } /** * Create a zero state Bloch vector */ static zero() { return BlochVector.from(BlochVector.ZERO) } /** * Utility function to create a bloch vector in many ways * * - `BlochVector.from(x, y, z)` creates a Bloch vector from x, y, z coordinates * - `BlochVector.from([x, y, z])` creates a Bloch vector from an array of coordinates * - `BlochVector.from(new Vector3(x, y, z))` creates a Bloch vector from a three.js Vector3 * - `BlochVector.from(new BlochVector(x, y, z))` creates a Bloch vector from another Bloch vector */ static from(x: number, y: number, z: number): BlochVector static from(y: Vector3): BlochVector static from(array: [number, number, number]): BlochVector static from(q: BlochVector): BlochVector static from( x: number | Vector3 | [number, number, number] | BlochVector, y: number = 0, z: number = 0 ) { if (Array.isArray(x)) { return new BlochVector(x[0], x[1], x[2]) } if (x instanceof BlochVector) { return x.clone() } if (x instanceof Vector3) { return new BlochVector(x.x, x.y, x.z) } return new BlochVector(x, y, z) } /** * Create a Bloch vector from angles (theta, phi) */ static fromAngles(theta: number, phi: number) { return BlochVector.zero().setAngles([theta, phi]) } /** The polar angle. The angle between the BlochVector and the z-axis */ get theta() { return Math.acos(this.z) } /** The azimuthal xy-plane angle. The angle between the projection of the BlochVector on the xy-plane and the x-axis */ get phi() { return normalizeAzimuthal(Math.atan2(this.y, this.x)) } /** The amplitude of the Bloch vector */ get amplitude() { return Math.sqrt(this.x ** 2 + this.y ** 2 + this.z ** 2) } /** The density matrix representation of the Bloch vector */ get rho() { return this.densityMatrix() } /** The density matrix representation of the Bloch vector */ densityMatrix() { const x = Complex.from(this.x) const y = Complex.from(this.y) const z = Complex.from(this.z) const half = Complex.from(0.5) const i = Complex.from(0, 1) return [ [half.times(z.plus(1)), half.times(x.minus(i.times(y)))], [half.times(x.plus(i.times(y))), half.times(Complex.ONE.minus(z))], ] } /** * Create a Bloch vector from a density matrix * * @param rho - The density matrix to create the Bloch vector from */ static fromDensityMatrix(rho: Complex[][]) { const x = rho[0][1].real * 2 const y = rho[1][0].imag * 2 const z = rho[0][0].minus(rho[1][1]).real return new BlochVector(x, y, z) } /** * Apply an operator to the Bloch vector returning a new Bloch vector * * @param op - The operator to apply * @returns The new Bloch vector */ applyOperator(op: Operator) { return BlochVector.fromDensityMatrix(op.applyTo(this.rho)) } /** * Get both angles of the Bloch vector as an array `[theta, phi]` */ angles(): [number, number] { return [this.theta, this.phi] } /** * Set the Bloch vector from angles `[theta, phi]` (polar, azimuthal) * * @param angles - The angles to set the Bloch vector to */ setAngles(angles: [number, number]) { const [theta, phi] = angles this.set( Math.sin(theta) * Math.cos(phi), Math.sin(theta) * Math.sin(phi), Math.cos(theta) ) return this } toString() { return `(${this.x}, ${this.y}, ${this.z})` } /** * Spherical linear interpolation of this Bloch vector to another Bloch vector * * @param other - The other Bloch vector to interpolate to * @param t - The interpolation factor (0 <= t <= 1) * @returns The interpolated Bloch vector */ slerpTo(other: BlochVector, t: number) { // using spherical interpolation const theta = lerpAngle(this.theta, other.theta, t) const phi = lerpAngle(this.phi, other.phi, t) return BlochVector.fromAngles(theta, phi) } }