New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@qctrl/visualizer

Package Overview
Dependencies
Maintainers
6
Versions
95
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@qctrl/visualizer

An animated and interactive quantum state visualisation built with THREE.js

  • 3.0.0-beta.6
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
150
decreased by-32.43%
Maintainers
6
Weekly downloads
 
Created
Source

Visualizer-Module (@qctrl/visualizer)

Animated visualization for 1-qubit and 2-qubit controls

Table of Contents

Overview

This is the base library/module of the Q-CTRL Visualizer. It is framework/library agnostic and can be implemented in a vanilla JavaScript app/website

Installation

Via NPM and ES6 import

  • Login to npm from the command line: npm login your npm account must be linked and have access to the qtrl org (This will be unnecessary if the package becomes publicly available)
  • Install the Visualizer module into your project with yarn add @qctrl/visualizer or npm i @qctrl/visualizer
  • Since the Visualizer is an npm module you will need to use the ES6 modules import feature and will also need a transpiler such as Babel and/or a build pipeline such as Parcel (easy) or Webpack (Advanced)
  • Import the Visualizer component into your App like so: import Visualizer from '@qctrl/visualizer'

Via direct download of pre-built bundle

  • Clone or download the repo and find the file Visualizer-Module/build/visualizer.js (or download/copy the built code directly from here)

  • Copy Visualizer.js to your project and include it in your main .html file, above your main/index .js file using a <script> tag like so:

    <!-- at the bottom inside of the body of your main index.html file -->
      ...
      <script src="path/to/visualizer.js"></script>
      <script src="path/to/index.js"></script>
    </body>
    

Quick Start Guide

Vanilla JavaScript implementation

  • In your project's JavaScript, get a reference to a <div> element/DOM-Node that will be used as a wrapper for the visualizer (or create a new <div> element). The Element should have width and height CSS properties set as the visualizer will use the wrapping <div> width and height parameters to set it's own size parameters internally. Note: The wrapper element will have its style property position set to relative by the visualizer

    <!-- inside the body of your main index.html file -->
    <body>
      ...
      <div id="visualizer-wrapper" style="width: 500px; height: 500px;"></div>
      ...
    </body>
    
    const visualizerWrapper = document.getElementById("visualizer-wrapper");
    
  • Next create a new Visualizer instance, passing in the wrapper reference, then initialize it like so:

    const myVisualizer = new Visualizer({ wrapper: visualizerWrapper }).init();
    
  • This will create and initialize a new Visualizer object and store a reference to it in the myVisualizer variable. Also, a canvas/DOM element that the visualizer will be rendered to will be attached to the passed in wrapper element

  • If you now preview the HTML file you should be able to see a single Bloch sphere on the page

  • A Visualizer instance has an update method that can be used to pass in new "props" or settings, for example you can set whether the animation is playing or not with the isPlaying prop. To try that now, below from where you have created your Visualizer instance, use this code snippet to have the visualizer start playing when the page loads:

    myVisualizer.update({ isPlaying: true });
    
  • Now when your HTML preview loads the Visualizer will start playing immediately. Since no data was provided to the Visualizer instance when you created it, it will have generated some default data: A single 180 degree rotation around the x axis. (If you have predefined data you can pass it in to the constructor when creating a new Visualizer instance and this will be used for the visualization. See the section on Data) for more info

  • If you do not have access to data you can instead pass in an array or a comma separated string of basic gate types. To try this, where you have initialized the visualizer instance update the code like so:

    const myVisualizer = new Visualizer({
      wrapper: visualizerWrapper,
      gates: ["X", "H", "Z", "T"]
    }) // or "X,H,Z,T" would also be valid
      .init();
    

    The Visualizer will generate the correct data to animate from the gate sequence you passed in. Now when you preview your HTML you will see an animated visualization of the above gate sequence. See Gates for more info and available gate types

Creating some basic playback controls

You can use a <button> element and a range type <input> element to create basic animation playback controls

Creating a Play/Pause button
  • First, get a reference to a button element and then add an onClick event listener/callback to it that will update the visualizer's isPlaying prop like so:

    <!-- inside the body of your main index.html file -->
    <body>
      ...
      <button id="play-button">Play/Pause</button>
      ...
    </body>
    

    At the top of your javascript, below where you have created a reference to your wrapper element get the reference to the button element and create a variable isPlaying that will be the reference for the isPlaying state in your app:

    const playButton = document.getElementById("play-button");
    
    let isPlaying = false;
    

    Then, below where you have initialized your visualizer, remove the earlier call to myVisualizer.update() and replace with this code snippet:

    playButton.addEventListener("click", () => {
      isPlaying = !isPlaying;
      myVisualizer.update({ isPlaying });
    });
    

Now when you load your HTML preview there will be a clickable button to start and stop the visualization animation

Creating a playback indicator (play bar)

The play bar will have the ability to scrub or jump to a particular place in the animation as well as visually indicating the position/progress of the animation

  • For this you will need to create a callback function and pass it into the visualizer constructor using the onUpdate prop. The callback you pass in will be called every browser animation frame and will be called with an event argument containing a reference to the visualizer instance itself (for convenient access to the update method) and a data object containing some specific and useful information such as the progress (calculated from the visualizer's current internal animation frame). See onUpdate for more info

  • Create and pass in the onUpdate callback like so Note: Replace the previous code where you created and initialized a new Visualizer instance with the following:

    const visualizerOnUpdate = event => {
      // You will fill out the body of this function further on in this guide
    };
    const myVisualizer = new Visualizer({
      wrapper: visualizerWrapper,
      gates: ["X", "H", "Z", "T"],
      onUpdate: visualizerOnUpdate // Passing in the new onUpdate callback here
    }).init();
    
  • Now, get a reference to a range type input element (slider). Since the progress is represented as a percentage in decimal format, the <input> element will need a min of 0 and max of 1 along with an appropriate step value for it to function properly (you could also use 0 to 100 and then convert the value back to a decimal percentage before passing it as progress into the visualizer update method):

    <!-- inside the body of your main index.html file -->
    <body>
      ...
      <input id="play-bar" type="range" min="0" max="1" step="0.01" />
      ...
    </body>
    

    At the top of your javascript, below where you have created a reference to your button element, get a reference to your input element and create a variable progress that will be the reference for the progress state in your app:

    const playBar = document.getElementById("play-bar");
    
    let progress = 0;
    
  • Back where you wrote the onUpdate callback function, fill the body of the function in like so:

    const visualizerOnUpdate = event => {
      progress = event.data.progress; // Update the progress state variable
      // with the new progress passed into the
      // onUpdate callback from the visualizer
      playBar.value = progress; // Set the value of the play bar input element
      // to match the progress being returned from the visualizer
    };
    
  • Next, to set up the ability to jump or 'scrub' through the animation, below where you added the click event listener to the button, add an "on input" event listener to the playBar element like so:

    playBar.addEventListener("input", e => {
      progress = +e.target.value; // get the value from the play bar input element,
      // the value must be coerced into a float (done here simply using the "+" operator)
      myVisualizer.update({ progress }); // update the visualizer with the new progress
    });
    
  • Finally, to have the play bar reset to the start when the animation has completed, update the event listener for the play/pause button like so:

    playButton.addEventListener("click", () => {
      isPlaying = !isPlaying;
    
      if (progress >= 1) {
        isPlaying = true;
        progress = 0; // Reset progress when clicking play again after the animation has completed
        // Note: isPlaying will get ser to false internally when the animation has completed
      }
      myVisualizer.update({ isPlaying, progress });
    });
    

Now when you load your HTML preview, you will see a slider, it's position/value will update based on the progress of the animation after clicking the play/pause button. You can also click on the slider or drag the handle to move or "scrub" through the animation

Final javascript for reference:
const visualizerWrapper = document.getElementById("visualizer-wrapper");
const playButton = document.getElementById("play-button");
const playBar = document.getElementById("play-bar");

let isPlaying = false;
let progress = 0;

const visualizerOnUpdate = event => {
  progress = event.data.progress;
  playBar.value = progress;
};

const myVisualizer = new Visualizer({
  wrapper: visualizerWrapper,
  gates: ["X", "H", "Z", "T"],
  onUpdate: visualizerOnUpdate
}).init();

playButton.addEventListener("click", () => {
  isPlaying = !isPlaying;

  if (progress >= 1) {
    isPlaying = true;
    progress = 0;
  }
  myVisualizer.update({ isPlaying, progress });
});

playBar.addEventListener("input", e => {
  progress = +e.target.value;
  myVisualizer.update({ progress });
});

API

Exports (NPM package)

AVAILABLE_CONFIGURATIONS

If using the build file this will be available as .AVAILABLE_CONFIGURATIONS on the Visualizer instance

An object whose values are the names of the currently available layout configurations. These configurations are used internally based on the data passed in however you can explicitly set the layout with the configurationName prop

{
  "ONE_QUBIT": "oneQubit",
  "TWO_QUBIT_ENTANGLEMENT": "twoQubitEntanglement"
}
AVAILABLE_CAMERA_PRESETS

If using the build file this will be available as .AVAILABLE_CAMERA_PRESETS on the Visualizer instance

An array of names of available camera preset positions that can be used with the cameraPreset prop

["XY", "YZ", "XZ", "DEFAULT"];
AVAILABLE_THEME_CONTROLS

If using the build file this will be available as .AVAILABLE_THEME_CONTROLS on the Visualizer instance

An array of objects (collection) of all available theming settings. This can be used to build a UI for controlling the theme settings that get passed in on the themeSettings prop. Each object will have at least a name and a type key:value. The name value corresponds to and matches with the name that should be passed in on the themeSettings prop to control/change that setting. The type is the type of suggested <input> and expected value for the themeSettings prop. See Styling and Theming for more info. Depending on the type there may be more values in the "available control" object, see below.

Theme Control Types

color

  • Recommended to be used with a color input/color picker that returns CSS color string values

rangeValue

  • Recommended to be used with a range (slider) input, includes min max and step values to use for the range input

numberValue

  • Recommended to be used with a number input, includes min max and step values to use for the number input

boolean

  • Recommended to be used with a toggle like control such as a checkbox input

Below is a subset of the currently available theme controls as an example of what they will look like:

[
  {
    name: "elementMeshColor",
    type: "color"
  },
  {
    name: "outerLightIntensity",
    type: "rangeValue",
    min: 0,
    max: 15,
    step: 0.01
  },
  {
    name: "pathWeight",
    type: "numberValue",
    min: 1,
    max: 10,
    step: 1
  },
  {
    name: "showOutlines",
    type: "boolean"
  }
];
default: Visualizer class / constructor

If using the build file this will be available as a global var (attached to the window object as a property) as Visualizer

This is the default export from the library and is used with the new keyword to create a new visualizer instance

Props (constructor / .init() / .update())

Visualizer Control Props

These props can be used to control the playback, camera and label display of the visualization

cameraPreset: <string>

Can be passed to: constructor, .init(), .update()

Sets the camera/3D view to a preset axial position

"XY" "YZ" "XZ" "XYZ" "DEFAULT"

isPlaying: <boolean> default: false

Can be passed to: constructor, .init(), .update()

Tells the Bloch sphere component to start or stop the animation

Note:*isPlaying will be set to false internally when the visualization animation reaches the last frame*

labels: <object<JSON>>

Can be passed to: constructor, .init(), .update()

Set the visibility of individual labels

JSON structure:

{
  "xAxis": <boolean>,
  "yAxis": <boolean>,
  "zAxis": <boolean>,
  "theta": <boolean>,
  "phi": <boolean>,
  "northPole": <boolean>,
  "southPole": <boolean>,
  "nonErrorState": <boolean>
}
progress: <float ( >= 0 <= 1 )> default: 0

Can be passed to: constructor, .init(), .update()

Sets the progress of the animation as a percent in decimal format (0 - 1). The animation frame to display will be updated accordingly. The actually frame index will be calculated internally based off the progress

Callbacks

getInitialThemeSettings: <function>

Can be passed to: constructor

This callback is handy if you are trying to sync UI elements in your app to match the initial values for the theme settings on page/component load. This callback will get all the initial settings for any available theme controls. If passed in to the Visualizer constructor when creating a new instance, the callback will be fired once during the calling of the constructor. It will be called with an object as the only argument and the object will contain key:values of the initial themeable settings.

onUpdate: <function>

Can be passed to: constructor, .init()

This callback, if passed in will be fired each browser animation cycle and is called with an event object:

  • event.target is a handy reference to the visualizer instance

  • event.data contains immediately accessible info/data about the state of the visualizer instance:

    {
      currentSegmentNumber, // Which segment number is currently being animated
        frameIndex, // Current animation frame/index
        progress, // Current animation progress (0 - 1)
        isInteracting, // If user is interacting with the visualizer
        // by dragging the mouse on an element
        isHovering; // If user is hovering the mouse over a path segment
      // or label / indicator with tooltip
    }
    

visualizationData: <object<JSON>>

Can be passed to: constructor, .update()

There can be two possible keys of the visualizationData object representing sub objects containing data sets:

  • data which will be the standard data set or the error state data set if you pass in a nonErrorStateData set. This key is required
  • nonErrorStateData Is an optional data set that if provided, will be visualized as a secondary indicator with a path between it and the main indicator to show decoherence. Same structure as the main data object. Note that the names of the qubits and vectors in nonErrorStateData must match with those on the data key

Visualization data passed in on either the data key or the nonErrorStateData key must be of a particular format which will be detailed below.

NOTE: The layout configuration for the visualization is determined automatically based on the number of qubits under the qubits key, unless a custom configuration is being passed in

NOTE: If updating data in the .update() method, the names of the qubits and vectors must match with those in the first data set passed in to the constructor.

Data Keys

qubits required: <array<object>>

  • qubits is the only required key

  • qubits data will be visualized using bloch spheres

  • qubits must be an array of objects (a collection), each object must have the following format:

    { "name": "<qubitName>", "x": [], "y": [], "z": [] }
    
    • Where the x y and z key values are arrays, that must be of equal length, of the plot points to be visualized, such that a complete plot point vector will be for example: (x[0], y[0], z[0])
    • The name key value can be any camel cased name
    • Note: The order of multiple qubits in the array will be the order in which they are rendered in the Visualization/to the screen, from left to right

vectors: <array<object>>

  • Vectors represent entangled states between two qubits and will be visualized on tetrahedra
  • vectors must follow the same format as qubits (see above)
  • Data for the first vector should represent x: XY y: YZ z: ZX
  • Data for the second vector should represent x: XZ y: YX z: ZY
  • Data for the third vector should represent x: XX y: YY z: ZZ
  • Multiple vectors will be rendered from left to right, top to bottom in the order that they are in in the vectors array

segmentIndexes: <array<integer>>

  • This must be an array of integers starting at 1 (0 will not be highlighted as a segment)
  • The array length must be equal to the array lengths of the plot points arrays for qubits and vectors
  • Segments will be highlighted as they are being animated

entanglementBooleans: <array<boolean>>

  • This must be an array of booleans and will be visualized by an entanglement link icon, showing whether there is an entangled state between two qubits
  • The array length must be equal to the array lengths of the plot points arrays for qubits and vectors

entanglementMeasures: <array<float ( >= 0 <= 1 )>>

  • This must be an array of floats between 0 and 1, representing the percentage of entanglement, visualized by an animated and gradated bar
  • The array length must be equal to the array lengths of the plot points arrays for qubits and vectors

Below is an example (excluding the actual data points) of the full data object structure for a two qubit visualization with entanglement:

{
  "qubits": [
    {
      "name": "<qubit_1_name>",
      "x": [],
      "y": [],
      "z": []
    },
    {
      "name": "<qubit_2_name>",
      "x": [],
      "y": [],
      "z": []
    }
  ],
  "vectors": [
    {
      "name": "<vector_a_name>",
      "x": [], //xy
      "y": [], //yz
      "z": [] //zx
    },
    {
      "name": "<vector_b_name>",
      "x": [], //xz
      "y": [], //yx
      "z": [] //zy
    },
    {
      "name": "<vector_c_name>",
      "x": [], //xx
      "y": [], //yy
      "z": [] //zz
    }
  ],
  "segmentIndexes": [],
  "entanglementBooleans": [],
  "entanglementMeasures": []
}

Other Props

configurationName: <string>

Can be passed to: constructor

You can pass in one of the available configurations names as a string to explicitly set the desired layout configuration. This will override the configuration that is automatically chose n based on the data

continuousSegments: <boolean> default: true

Can be passed to: constructor, .init(), .update()

If set to false, a path/line piece will not be created/drawn between each segment (useful if there are non-contiguous segment indexes in a data set. Note that if passing this prop into .update(), it will only have an effect if updating the data on the same call to .update()

drawArcs: <boolean> default: false

Can be passed to: constructor, .init(), .update()

If set to true, when creating the pulse path, an arc of extra points/vectors will be generated and drawn between each data point instead of directly connecting each point with a straight line. Useful if using a sparse set of data, where the distance between each data point is large (for example with certain noise or slepian visualizations). Note that if passing this prop into .update(), it will only have an effect if updating the data on the same call to .update()

gates: <array<string>> | <string>

Can be passed to: constructor, .init()

Instead of a predefined data object you can pass in an array or a comma separated string of basic gate types and the Visualizer will generate the correct data to animate from the gate sequence. Note that if you pass in both data and gates as props the data will be used and the gates ignored.

NOTE: Currently only works for a one qubit visualization

Possible gate operations are (case insensitive):

  • X
  • Y
  • Z
  • H
  • S
  • S_DAG (S†, The conjugate transpose of the S gate)
  • T
  • T_DAG (T†, the conjugate transpose of the T gate)

Example gates array:

["X", "H", "Z", "T", "S_DAG"];

Example gates string:

"X,H,Z,T,S_DAG";
inputState: <object<JSON>>

Can be passed to: constructor, .init(), .update()

This will set an arbitrary starting / position for the pulse indicators

JSON structure:

{
  "qubits": [
    { "name": "qubit1", "x": 0, "y": 1, "z": 0 },
    { "name": "qubit2", "x": 0, "y": 0, "z": -1 }
  ],
  "vectors": [{ "name": "a", "x": 0, "y": 0, "z": -1 }]
}
layoutConfiguration: <object<HTMLElement | DOMNode>>

Can be passed to: constructor, .init()

Provide a custom layout to override the internally generated one. Each key should be a string matching a "qubit" or "vector" name in the data provided, the values should be div elements with both their CSS properties "grid-area" and their id attributes set to the same name as the key

style: <object<JSON>>

Can be passed to: constructor

  • See the file ./src/configuration/styleDefaults.js to see what structure layout settings need to be in
  • If no style prop is passed, all default settings will be used from styleDefaults.js
  • You are able to pass a subset of layout settings and these will be merged with the rest of the default settings from the settings file, no need to pass all the settings if you only need to customize a few things

See Styling and Theming for more info

themeSettings: <object>

Can be passed to: .update()

The themeSettings prop is used to dynamically change certain visual styles of the visualizer instance. In effect you can create your own color and visual theme for your visualization by adjusting these settings. pass in one or more key:value pairs where the key is the name of an available theme setting and the value is the new value you would like to set for that setting

Possible theme settings with types:
{
  "outerLightIntensity": <float>,
  "innerLightIntensity": <float>,
  "backgroundColor": <string:cssColor>,
  "indicatorColor": <string:cssColor>,
  "pathColor": <string:cssColor>,
  "highlightColor": <string:cssColor>,
  "nonErrorStateDotColor": <string:cssColor>,
  "metalness": <float>,
  "roughness": <float>,
  "opacity": <float>,
  "elementMeshColor": <string:cssColor>,
  "pathWeight": <integer>,
  "showOutlines": <boolean>
}

See available theme controls for information on how to get references to theme control types and accepted ranges for control values

wrapper required: <HTMLElement | DOMNode>

Must be passed to either: constructor or .init()

This is the only required prop for a new visualizer instance. It must be a valid HTML element/DOM node. The width and height style/CSS properties must be set as the visualizer will use the wrapper width and height parameters to set it's own size parameters internally. It will then attach the canvas/rendering context to it. Note: The wrapper element will have its style property position set to relative by the visualizer

Available Visualizer instance Methods

.init({ ...props })

Once you have created a new Visualizer instance, you must call the .init() method to start the internal renderer and of the visualizer

Note: This method will only ever fire once regardless of how many times it is called. This is handy if you are using or developing a wrapping component using a particular front-end framework or library that uses life cycle methods and re-renders and you only have access to a valid DOM node/ref for the wrapper on the first component update (after any mounting cycles). You can call .init() in the update cycle with the valid ref and not need to worry about creating a conditional to only call it once.

Valid props:

isPlaying labels progress onUpdate gates inputState layoutConfiguration wrapper

Note: Props that are valid for both the constructor and .init() should only be passed into one or the other

.update({ ...props })

This is the method used to control the the play back state including progress, labels state, camera preset and visual theme settings

Valid props:

isPlaying labels progress inputState cameraPreset themeSettings

.getProgressFromFrameIndex()

You can use this method to get the current progress of the visualizer animation as a decimal percent without relying on the onUpdate() callback. This Method takes no arguments.

.cleanup()

This method should be called if you are replacing a visualizer instance with a new one (like unmounting and re-mounting), such as you would with a single page application. By calling this method before loosing the reference to the visualizer instance, a cleanup routine, disposing of expensive 3D geometry in the 3d context, will be run. This can help with memory and performance issues. This Method takes no arguments.

Styling and Theming

Development

JSDocs

Dev/Build/Publish Workflow

FAQs

Package last updated on 05 Jul 2019

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc