UNPKG

@qbead/bloch-sphere

Version:

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

934 lines (908 loc) 26.6 kB
import * as THREE from 'three'; import { Quaternion, Vector3, ColorRepresentation } from 'three'; import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js'; import { OrbitControls } from 'three/examples/jsm/Addons.js'; /** * Abstract base class for all components */ declare class BaseComponent extends THREE.Object3D { protected _color: THREE.Color; constructor(name?: string); /** * Get color of the component */ get color(): THREE.ColorRepresentation; /** * Set color of the component */ set color(color: THREE.ColorRepresentation); } /** * Label component for displaying text in 3D space * * @example * ```typescript * const label = new Label('Hello World'); * label.position.set(0, 1, 0); * label.color = 'red'; * label.fontSize = 2; * blochSphere.add(label); * ``` * * @extends BaseComponent */ declare class Label extends BaseComponent { private htmlobj; /** * Create a new label * @param text The text to display * @param type The type of label, corresponding to the html class (default: 'label') */ constructor(text: string, type?: string); get text(): string; set text(text: string); get fontSize(): number; set fontSize(size: number); get color(): THREE.Color; set color(color: THREE.ColorRepresentation); /** * Cleanup tasks */ destroy(): void; } declare const BlockSphereSceneOptions: { backgroundColor: THREE.Color; gridColor: THREE.Color; gridDivisions: number; sphereSkinColor: THREE.Color; sphereSkinOpacity: number; }; /** * A scene for the Bloch sphere which extends the THREE.Scene class */ declare class BlochSphereScene extends THREE.Scene { sphere: THREE.Group; grids: THREE.Group; axes: THREE.Group; labels: Record<string, Label>; plotStage: THREE.Group; constructor(options?: Partial<typeof BlockSphereSceneOptions>); get backgroundColor(): THREE.Color; set backgroundColor(color: THREE.ColorRepresentation); private initLabels; clearPlot(): void; } /** * A type that can be converted to a Complex number. */ type IntoComplex = Complex | number | [number, number]; /** * A class representing a complex number. */ declare class Complex { real: number; imag: number; static get ZERO(): Complex; static get ONE(): Complex; static get I(): Complex; constructor(real: number, imag?: number); static from(value: IntoComplex): Complex; static from(real: number, imag?: number): Complex; static random(): Complex; static unitRandom(): Complex; static fromPolar(magnitude: number, phase: number): Complex; copy(other: Complex): this; clone(): Complex; plus(other: IntoComplex): Complex; minus(other: IntoComplex): Complex; times(other: IntoComplex): Complex; dividedBy(other: IntoComplex): Complex; get magnitude(): number; get phase(): number; conjugate(): Complex; reciprocal(): Complex; pow(exponent: number): Complex; sqrt(): Complex; toString(): string; } /** * Quantum operators are 2x2 matrices of complex numbers * * ``` * [ a b ] * [ c d ] * ``` */ declare class Operator { elements: Complex[][]; static identity(): Operator; constructor(elements: Complex[][]); /** * The first row, first column element of the operator */ get a(): Complex; /** * The first row, second column element of the operator */ get b(): Complex; /** * The second row, first column element of the operator */ get c(): Complex; /** * The second row, second column element of the operator */ get d(): Complex; copy(other: Operator): this; clone(): Operator; /** * Multiply the operator by a scalar */ scale(scalar: number): this; /** * Multiply the operator by another operator */ times(other: Operator): Operator; /** * Get the conjugate transpose of the operator as a new operator */ conjugateTranspose(): Operator; /** * Apply this operator to a density matrix */ applyTo(rho: Complex[][]): Complex[][]; /** * Add another operator to this operator */ plus(other: Operator): Operator; /** * Get the determinant of the operator */ determinant(): Complex; /** * Get this operator as a THREE.Quaternion */ quaternion(): Quaternion; } /** * 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() * ``` */ declare class BlochVector extends Vector3 { /** * A bloch vector representing the zero state */ static get ZERO(): BlochVector; /** * A bloch vector representing the one state */ static get ONE(): BlochVector; /** * A bloch vector representing the plus state (|+>) or (|0> + |1>)/sqrt(2) */ static get PLUS(): BlochVector; /** * A bloch vector representing the minus state (|->) or (|0> - |1>)/sqrt(2) */ static get MINUS(): BlochVector; /** * A bloch vector representing the imaginary state (|i>) or (|0> + i|1>)/sqrt(2) */ static get I(): BlochVector; /** * A bloch vector representing the minus imaginary state (|-i>) or (|0> - i|1>)/sqrt(2) */ static get MINUS_I(): BlochVector; /** * Generate a random Bloch vector with magnitude 1 */ static random(): BlochVector; /** * Create a zero state Bloch vector */ static zero(): BlochVector; /** * 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; /** * Create a Bloch vector from angles (theta, phi) */ static fromAngles(theta: number, phi: number): BlochVector; /** The polar angle. The angle between the BlochVector and the z-axis */ get theta(): number; /** The azimuthal xy-plane angle. The angle between the projection of the BlochVector on the xy-plane and the x-axis */ get phi(): number; /** The amplitude of the Bloch vector */ get amplitude(): number; /** The density matrix representation of the Bloch vector */ get rho(): Complex[][]; /** The density matrix representation of the Bloch vector */ densityMatrix(): Complex[][]; /** * Create a Bloch vector from a density matrix * * @param rho - The density matrix to create the Bloch vector from */ static fromDensityMatrix(rho: Complex[][]): BlochVector; /** * 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): BlochVector; /** * Get both angles of the Bloch vector as an array `[theta, phi]` */ angles(): [number, number]; /** * Set the Bloch vector from angles `[theta, phi]` (polar, azimuthal) * * @param angles - The angles to set the Bloch vector to */ setAngles(angles: [number, number]): this; toString(): string; /** * 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): BlochVector; } /** * Camera state for the Bloch sphere */ type CameraState = { /** Angle from Z-axis in radians (0 to π) - polar angle */ theta?: number; /** Angle from X-axis in XY-plane in radians (0 to 2π) - azimuthal angle */ phi?: number; /** Camera zoom level (1.0 = default, >1 = zoomed in, <1 = zoomed out) */ zoom?: number; }; /** * Interactivity options for OrbitControls */ type InteractivityOptions = { /** Enable/disable zoom via mouse wheel/pinch */ zoom?: boolean; /** Enable/disable camera rotation via mouse drag */ rotate?: boolean; }; /** * Options for the Bloch Sphere widget */ type BlochSphereOptions = { /** font size in em */ fontSize?: number; /** show grid */ showGrid?: boolean; /** initial camera state */ cameraState?: CameraState; /** Enable/disable all user interactions (default: true) */ interactive?: boolean; /** Enable/disable zoom via mouse wheel/pinch (default: true) */ enableZoom?: boolean; /** Enable/disable camera rotation via mouse drag (default: true) */ enableRotate?: boolean; } & Partial<typeof BlockSphereSceneOptions>; /** * A Bloch Sphere Widget * * This class is a wrapper around the THREE.js WebGLRenderer and CSS2DRenderer * to create a Bloch sphere visualization. * * It provides methods to add and remove objects from the scene. * * It must be attached to a parent element in the DOM to be visible. It will * automatically resize to fit the parent element. * * To resize on window resize, you can call the `resize` method in the * window resize event listener. * * @example * ```ts * import { BlochSphere } from 'bloch-sphere' * * const blochSphere = new BlochSphere({ * fontSize: 1.25, * }) * * blochSphere.attach(document.body) * window.addEventListener( * 'resize', * () => { * blochSphere.resize() * }, * { passive: true } * ) * * // create a qubit display * const qubit = new QubitDisplay(BlochVector.fromAngles(1, 0)) * // add the qubit to the Bloch sphere * blochSphere.add(qubit) * ``` */ declare class BlochSphere { renderer: THREE.WebGLRenderer; cssRenderer: CSS2DRenderer; el: HTMLElement; scene: BlochSphereScene; camera: THREE.OrthographicCamera; controls: OrbitControls; private _cameraAnimation; constructor(options?: BlochSphereOptions); setOptions(options?: BlochSphereOptions): void; private initRenderer; get showGrid(): boolean; set showGrid(value: boolean); add(item: THREE.Object3D): void; remove(item: THREE.Object3D): void; /** * Removes all objects from the plot * * This will not remove the grid or the sphere. */ clearPlot(): void; /** * Rescales the sphere */ scale(size: number): void; /** * Attaches the widget to a parent element * * Must be called to make the widget visible. */ attach(parent?: HTMLElement): void; /** * Resizes the widget to fit the parent element * * Optionally, you can specify the width and height to resize to. */ resize(width?: number, height?: number): void; /** * Renders the scene * * This is called automatically in the animation loop unless that * loop is stopped. */ render(): void; /** * Starts the animation loop * * Automatically started when the widget is attached to a parent element. * * This will call the render method automatically. */ start(): void; /** * Stops the animation loop * * This will stop the render loop */ stop(): void; /** * Core method to set camera state with optional animation * This is the single source of truth for camera positioning - other methods delegate to this */ private _setCameraState; /** * Immediately apply camera state without animation */ private _applyCameraState; /** * Get current camera zoom level */ getCameraZoom(): number; /** * Get current camera angles as [theta, phi] */ getCameraAngles(): [number, number]; /** * Get the Bloch vector pointing from origin to camera */ getCameraBlochVector(): BlochVector; /** * Set camera state (unified method) */ setCameraState(cameraState: CameraState, duration?: number, easing?: string): Promise<void> | void; /** * Position camera such that the given Bloch vector points directly at camera */ setCameraToBlochVector(blochVector: BlochVector, duration?: number, easing?: string): Promise<void> | void; /** * Set camera position using spherical coordinates */ setCameraAngles(theta: number, phi: number, duration?: number, easing?: string): Promise<void> | void; /** * Set camera zoom level */ setCameraZoom(zoomLevel: number, duration?: number, easing?: string): Promise<void> | void; /** * Control user interactivity with the camera * * @param options - Interactivity options or boolean to enable/disable all interactions * @returns Current interactivity state if no arguments provided */ interactivity(options?: InteractivityOptions | boolean): InteractivityOptions; /** * Performs cleanup and disposes everything contained in the widget */ dispose(): void; } /** * A display for just a qubit arrow * * @see {@link QubitDisplay} for a full qubit display * * @example * ```ts * const q = new BlochVector(0, 0, 1) * const arrow = new QubitArrow() * arrow.color = 0xe1b53e * arrow.follow(q) * blochSphere.add(arrow) * ``` */ declare class QubitArrow extends BaseComponent { private arrowHelper; label: Label; constructor(); set color(color: THREE.ColorRepresentation); follow(v: BlochVector): void; } type DegreesUnits = 'deg' | 'DEG' | 'degrees'; type RadiansUnits = 'rad' | 'RAD' | 'radians'; type AngleUnits = DegreesUnits | RadiansUnits; /** * Display angle indicators for a Bloch vector * * @example * ```ts * const angleIndicators = new AngleIndicators() * angleIndicators.update(blochVector) * angleIndicators.color = 0xe1b53e * blochSphere.add(angleIndicators) * ``` */ declare class AngleIndicators extends BaseComponent { units: AngleUnits; private phiWedge; private phiLabel; private thetaLabelContainer; private thetaWedge; private thetaLabel; private phiLabelContainer; private _phiColor; private _thetaColor; /** * Creates a new AngleIndicators component * * @param scale - The scale of the angle indicators (default is 0.25) */ constructor(scale?: number); /** * Update the angle indicators for the given Bloch vector */ update(v: BlochVector): void; get opacity(): number; set opacity(opacity: number); /** * The distance of the labels from the center of the sphere */ get labelRadius(): number; set labelRadius(radius: number); set color(color: THREE.ColorRepresentation); get phiColor(): THREE.ColorRepresentation; set phiColor(color: THREE.ColorRepresentation); get thetaColor(): THREE.ColorRepresentation; set thetaColor(color: THREE.ColorRepresentation); } /** * Creates a wedge, that is the outline of * a quarter of a circle. */ declare class Wedge extends BaseComponent { constructor(); } /** * A wedge which is a quarter of a circle */ declare class QubitProjWedge extends Wedge { constructor(); follow(v: BlochVector): void; } type AnimationCallback = (progress: number) => void; /** @deprecated Use CancellablePromise instead */ type CancelAnimation = () => void; interface CancellablePromise<T> extends Promise<T> { cancel(): void; } /** * Animation helper function * * This helper will set up an animation loop and call the callback function * with the progress of the animation. * * Returns a promise that resolves when the animation completes. The promise * has a cancel() method that can be called to stop the animation early. * If the callback throws an error, the promise will reject with that error. * * @param callback - The function to call with the progress of the animation. * @param duration - The duration of the animation in milliseconds (default is 1000). * @param easing - The easing function to use (default is 'linear'). * @param loop - Whether to loop the animation (default is false). Looping animations never resolve unless cancelled. * @returns A cancellable promise that resolves when the animation completes or rejects if the callback throws an error. * * @example * ```ts * // await animation completion * await animate((progress) => { * myElement.style.opacity = progress * }, 1000, 'easeInOut') * * // looping animation with cancellation * const anim = animate((progress) => { * myElement.style.opacity = progress * }, 1000, 'easeInOut', true) * // later... * anim.cancel() * * // handle errors * try { * await animate((progress) => { * if (someCondition) throw new Error('Animation failed') * myElement.style.opacity = progress * }, 1000) * } catch (error) { * console.error('Animation error:', error) * } * ``` */ declare function animate(callback: AnimationCallback, duration?: number, easing?: string, loop?: boolean): CancellablePromise<void>; /** * A display for a qubit state on the Bloch sphere * * This component shows the arrow, angle indicators, and a label. * * @example * ```ts * const q = new BlochVector(0, 0, 1) * const qubit = new QubitDisplay(q) * qubit.color = 0xe1b53e * blochSphere.add(qubit) * ``` */ declare class QubitDisplay extends BaseComponent { arrow: QubitArrow; wedge: QubitProjWedge; angleIndicators: AngleIndicators; state: BlochVector; private _anim; constructor(q?: BlochVector); set color(color: ColorRepresentation); /** * Set the bloch vector state of the display * * Can also be used to animate the state of the qubit. * * @param q - The new Bloch vector state to set. * @param duration - The duration of the animation (default is 0). * @param easing - The easing function to use for the animation (default is 'quadInOut'). */ set(q: BlochVector, duration?: number, easing?: string): CancellablePromise<void> | undefined; } /** * A display for a path on the Bloch sphere * * The path is defined by a series of Bloch vectors. * * @example * ```ts * const path = new PathDisplay([ * BlochVector.fromAngles(0, 0), * BlochVector.fromAngles(1, 0.1), * BlochVector.fromAngles(1, 1), * ]) * path.color = 0xc33175 * blochSphere.add(path) * ``` */ declare class PathDisplay extends BaseComponent { constructor(path?: BlochVector[]); set(vertices: BlochVector[]): void; } /** * A display for a region on the Bloch sphere * * This component shows a spherical polygon on the Bloch sphere. * * @example * ```ts * const region = new RegionDisplay([ * BlochVector.fromAngles(0, 0), * BlochVector.fromAngles(1, 0.1), * BlochVector.fromAngles(1, 1), * ]) * region.color = 0xe1b53e * blochSphere.add(region) * ``` */ declare class RegionDisplay extends BaseComponent { private sphere; constructor(region?: BlochVector[]); get color(): THREE.ColorRepresentation; set color(color: THREE.ColorRepresentation); /** * Set the region of the display * * @param points - The bloch vectors that define the region. */ setRegion(points: BlochVector[]): void; } /** * A display for points on the Bloch sphere * * @example * ```ts * // generate 100 randomly * const points = new PointsDisplay( * Array.from({ length: 100 }, () => BlochVector.random()) * ) * points.color = 0xe1b53e * points.pointSize = 10 * blochSphere.add(points) * ``` */ declare class PointsDisplay extends BaseComponent { private pointMaterial; constructor(points?: BlochVector[]); /** * Set the size of the points */ get pointSize(): number; set pointSize(size: number); /** * Set the color of the points */ set color(color: THREE.ColorRepresentation); /** * Set the points to display */ set(points: BlochVector[]): void; } /** * A display for a quantum operator * * @example * ```ts * const op = gates.hadamard() * const display = new OperatorDisplay(op) * blochSphere.add(display) * ``` */ declare class OperatorDisplay extends BaseComponent { operator: Operator; innerGroup: THREE.Group; label: Label; private anim; constructor(op?: Operator); /** * Set the operator to display * @param op The operator to display */ set(op: Operator): void; /** * Perform cleanup tasks */ dispose(): void; } /** * A display for the path a qbit takes when it is rotated by an operator * @example * ```ts * const op = gates.hadamard() * const v = new BlochVector(0, 0, 1) * const display = new OperatorPathDisplay(v, op) * blochSphere.add(display) * ``` */ declare class OperatorPathDisplay extends BaseComponent { operator: Operator; vector: BlochVector; innerGroup: THREE.Group; path: THREE.Mesh; disc: THREE.Mesh; constructor(op?: Operator, v?: BlochVector); /** * Set the operator and vector */ set(op: Operator, v: BlochVector): void; /** * Set the operator */ setOperator(op: Operator): void; /** * Set the vector */ setVector(v: BlochVector): void; } /** * The identity operator */ declare function identity(): Operator; /** * The Pauli-X operator (also known as NOT or bit-flip operator) */ declare function x(): Operator; /** * Alias for the Pauli-X operator */ declare const not: typeof x; /** * The Pauli-Y operator */ declare function y(): Operator; /** * The Pauli-Z operator */ declare function z(): Operator; /** * The Hadamard operator * * Often used to create superposition states from the |0> state */ declare function hadamard(): Operator; /** * The phase operator * * Applies a phase of `phi` to the |1> state */ declare function phase(phi: number): Operator; /** * Rotation around the X axis * Applies a rotation of `theta` radians around the X axis */ declare function rx(theta: number): Operator; /** * Rotation around the Y axis * Applies a rotation of `theta` radians around the Y axis */ declare function ry(theta: number): Operator; /** * Rotation around the Z axis * Applies a rotation of `theta` radians around the Z axis */ declare function rz(theta: number): Operator; declare const gates_hadamard: typeof hadamard; declare const gates_identity: typeof identity; declare const gates_not: typeof not; declare const gates_phase: typeof phase; declare const gates_rx: typeof rx; declare const gates_ry: typeof ry; declare const gates_rz: typeof rz; declare const gates_x: typeof x; declare const gates_y: typeof y; declare const gates_z: typeof z; declare namespace gates { export { gates_hadamard as hadamard, gates_identity as identity, gates_not as not, gates_phase as phase, gates_rx as rx, gates_ry as ry, gates_rz as rz, gates_x as x, gates_y as y, gates_z as z, }; } /** * Properties of a arc on a sphere */ type ArcProperties = { /** * The radius of the arc */ radius: number; /** * The distance from the center of the sphere to the plane containing the arc */ height: number; /** * The normal vector of the plane containing the arc */ norm: THREE.Vector3; /** * The angle of the arc offset from the x-axis */ arcOffset: number; /** * The angle of the arc */ arcAngle: number; }; /** * Gets properties of an arc starting from v and rotating about n by angle */ declare function getRotationArc(v: THREE.Vector3, axis: THREE.Vector3, angle: number): ArcProperties; /** * Gets the properties of an great arc between two vectors * @param v1 The first vector * @param v2 The second vector * @returns The properties of the great arc */ declare function getArcBetween(v1: THREE.Vector3, v2: THREE.Vector3): { norm: THREE.Vector3; arcOffset: number; arcAngle: number; }; /** * Computes the shortest distance between two angles, considering wrap-around * @param a0 The first angle * @param a1 The second angle * @param modulo The modulo value (e.g., 2π for radians) * @returns The shortest distance from a0 to a1 */ declare function shortestModDist(a0: number, a1: number, modulo: number): number; type RotationInfo = { axis: THREE.Vector3; angle: number; }; /** * Get axis of rotation and angle from a quaternion * @param q The quaternion * @returns The axis of rotation */ declare function axisFromQuaternion(q: THREE.Quaternion): RotationInfo; /** * Standard linear interpolation function */ declare function lerp(a: number, b: number, t: number): number; /** * Linear interpolation function that wraps around 2π */ declare function lerpAngle(a: number, b: number, t: number): number; /** * Format a vector as a string with a given precision. */ declare function formatVector(v: Vector3, precision?: number): string; /** * Format an angle given in radians as a string in degrees * with a given precision. */ declare function formatDegrees(radians: number, precision?: number): string; /** * Format an angle given in radians as a string in radians * with a given precision. */ declare function formatRadians(radians: number, precision?: number): string; export { AngleIndicators, BaseComponent, BlochSphere, BlochSphereScene, BlochVector, Complex, Label, Operator, OperatorDisplay, OperatorPathDisplay, PathDisplay, PointsDisplay, QubitArrow, QubitDisplay, QubitProjWedge, RegionDisplay, Wedge, animate, axisFromQuaternion, formatDegrees, formatRadians, formatVector, gates, getArcBetween, getRotationArc, lerp, lerpAngle, shortestModDist }; export type { AnimationCallback, ArcProperties, BlochSphereOptions, CancelAnimation, CancellablePromise, IntoComplex, RotationInfo };