@qbead/bloch-sphere
Version:
A 3D Bloch Sphere visualisation built with Three.js and TypeScript.
797 lines (771 loc) • 22.1 kB
text/typescript
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;
}
/**
* Options for the Bloch Sphere widget
*/
type BlochSphereOptions = {
/** font size in em */
fontSize?: number;
/** show grid */
showGrid?: 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;
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;
/**
* Performs cleanup and disposes everything contained in the widget
*/
dispose(): 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;
plus(other: IntoComplex): Complex;
minus(other: IntoComplex): Complex;
times(other: IntoComplex): Complex;
dividedBy(other: IntoComplex): Complex;
get magnitude(): number;
get phase(): number;
conjugate(): 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;
/**
* 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 angle between the BlochVector and the z-axis */
get theta(): number;
/** 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[];
/**
* Set the Bloch vector from angles `[theta, phi]`
*
* @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;
}
/**
* 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;
}
/**
* 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): any;
}
/**
* 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, n: 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;
};
/**
* Standard linear interpolation function
*/
declare function lerp(a: number, b: number, t: number): number;
type AnimationCallback = (progress: number) => void;
type CancelAnimation = () => void;
/**
* Animation helper function
*
* This helper will set up an animation loop and call the callback function
* with the progress of the animation.
*
* A cancel function is returned that can be called to stop the animation.
*
* @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).
* @returns A function that can be called to cancel the animation.
*
* @example
* ```ts
* // looping animation that fades in and out
* const cancel = animate((progress) => {
* myElement.style.opacity = progress
* }, 1000, 'easeInOut', true)
* ```
*/
declare function animate(callback: AnimationCallback, duration?: number, easing?: string, loop?: boolean): CancelAnimation;
/**
* 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, type AnimationCallback, type ArcProperties, BaseComponent, BlochSphere, BlochSphereScene, BlochVector, type CancelAnimation, Complex, type IntoComplex, Label, Operator, OperatorDisplay, OperatorPathDisplay, PathDisplay, PointsDisplay, QubitArrow, QubitDisplay, QubitProjWedge, RegionDisplay, Wedge, animate, formatDegrees, formatRadians, formatVector, gates, getArcBetween, getRotationArc, lerp };