Skip to content

Commit

Permalink
Add more gates
Browse files Browse the repository at this point in the history
Fix update gate
  • Loading branch information
klezm committed Mar 7, 2024
1 parent 6f8efb8 commit 252f436
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 71 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"cSpell.words": ["raycaster"],
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.singleQuote": true
}
11 changes: 6 additions & 5 deletions src/buttonselector.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
export type Options<T> = { key: T; value: string }[];
export type OnClickCallback = (option: string) => void;
export type Options<T> = { key: T; value?: string; tooltip?: string }[];
export type OnClickCallback = (option: string, onlyMatrix?: boolean) => void | any;

function createHTML<T>(keyValues: { key: T; value: string }[]) {
function createHTML<T>(keyValues: Options<T>) {
let result = '';
for (let i = 0; i < keyValues.length; i++) {
const key = keyValues[i].key;
const value = keyValues[i].value;
result += `<button name="${key}">${value}</button>`;
const value = keyValues[i].value || keyValues[i].key;
const tooltip = keyValues[i].tooltip || '';
result += `<button name="${key}" title="${tooltip}">${value}</button>`;
}
return result;
}
Expand Down
90 changes: 70 additions & 20 deletions src/gateselector.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,82 @@
import * as math from 'mathjs';
import { ButtonSelector, OnClickCallback } from './buttonselector';
import { ketStr, Matrix2Latex, parse2DMatrix } from './utils';

export enum SelectedGate {
X = '$$X$$',
Y = '$$Y$$',
Z = '$$Z$$',
H = '$$H$$',
// https://docs.quantum.ibm.com/api/qiskit/circuit_library
// https://algassert.com/quirk
// https://en.wikipedia.org/wiki/List_of_quantum_logic_gates
X = 'X',
Y = 'Y',
Z = 'Z',
H = 'H',
RX = 'RX',
RY = 'RY',
SX = 'SX',
// T = 'T',
S = 'S',
Clear = 'Clear',
}

export const gate2Matrix: {
[key: string]: [string, [string, string], [string, string]];
} = {
[SelectedGate.X]: ['', ...parse2DMatrix('0 1 \\ 1 0')], // NOT, Bit-Flip
[SelectedGate.Y]: ['', ...parse2DMatrix('0 -i \\ i 0')],
[SelectedGate.Z]: ['', ...parse2DMatrix('1 0 \\ 0 -1')], // = T^4 Phase-Flip
[SelectedGate.H]: ['sqrt(1/2)', ...parse2DMatrix('1 1 \\ 1 -1')], // Hadamard

[SelectedGate.RX]: ['sqrt(1/2)', ...parse2DMatrix('1 -i \\ -i 1')], // θ=π/2 = exp(-i(θ/2)X)
[SelectedGate.RY]: ['sqrt(1/2)', ...parse2DMatrix('1 -1 \\ 1 1')], // θ=π/2 = exp(-i(θ/2)Y)
// [SelectedGate.RXdg]: ['sqrt(1/2)', ...parse2DMatrix('1 i \\ i 1')],
// [SelectedGate.RYdg]: ['sqrt(1/2)', ...parse2DMatrix('1 1 \\ -1 1')],

[SelectedGate.SX]: ['sqrt(1/2)', ...parse2DMatrix('1+i 1-i \\ 1-i 1+i')], // Sqrt of X
// [SelectedGate.SXdg]: ['sqrt(1/2)', ...parse2DMatrix('1-i 1+i \\ 1+i 1-i')],
// [SelectedGate.T]: ['', ...parse2DMatrix('1 0 \\ 0 exp(i*pi/4)')], // pi/8 // Equivalent to a π/4 radian rotation about the Z axis.
// [SelectedGate.Tdg]: ['', ...parse2DMatrix('1 0 \\ 0 exp(-i*pi/4)')], // Equivalent to a -π/4 radian rotation about the Z axis.
[SelectedGate.S]: ['', ...parse2DMatrix('1 0 \\ 0 i')], // = T^2 Phase // Equivalent to π/2 radian rotation about the Z axis.
// [SelectedGate.Sdg]: ['', ...parse2DMatrix('1 0 \\ 0 -i')], // Singleton // Equivalent to -π/2 radian rotation about the Z axis.

// [SelectedGate.P]: ['', ...parse2DMatrix('1 0 \\ 0 exp(i*λ)')], // λ=π=>Z λ=π/2=>S λ=π/4=>T Phase-Shift
// [SelectedGate.I]: ['', ...parse2DMatrix('1 0 \\ 0 1')], // Identity

[SelectedGate.Clear]: ['', ['', ''], ['', '']],
};
// const gate2Matrix: {
// [key: string]: [string, [string, string], [string, string]];
// } = Object.fromEntries(Object.entries(gate2Matrix_).map(([key, value]) => {
// if (isArray(value[1])) {return [key, value as [string, [string, string], [string, string]]];}
// return [key, [value[0], ...parse2DMatrix(value[1])]];
// }));

function latexButton(gate: SelectedGate) {
return `$$\\mathtip{
${gate}
}{
${(() => {
const scalar = gate2Matrix[gate][0];
const mx = math.evaluate(gate2Matrix[gate].slice(1, 3));
const latex = Matrix2Latex(mx, scalar, false);
return latex;
// const scalar = gate2Matrix[gate][0];
// const mx = gate2Matrix[gate].slice(1, 3);
// return Matrix2Latex(mx as unknown as math.MathArray, scalar, false);
})()}
}$$`;
}

export class GateSelector extends ButtonSelector<SelectedGate> {
constructor(p: HTMLElement, onClick: OnClickCallback) {
super(p, onClick, [
{
key: SelectedGate.X,
value: SelectedGate.X,
},
{
key: SelectedGate.Y,
value: SelectedGate.Y,
},
{
key: SelectedGate.Z,
value: SelectedGate.Z,
},
{
key: SelectedGate.H,
value: SelectedGate.H,
},
...Object.values(SelectedGate)
.filter((el, ix, ar) => el !== 'Clear')
.map((val) => {
return {
key: val,
value: latexButton(val),
};
}),
{
key: SelectedGate.Clear,
value: SelectedGate.Clear,
Expand Down
66 changes: 49 additions & 17 deletions src/lib.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { makeBloch, QuantumStateChangeCallback } from './bloch';
import { calculateOriantation, Matrix2x2 } from './eigen';
import { GateSelector, SelectedGate } from './gateselector';
import { gate2Matrix, GateSelector, SelectedGate } from './gateselector';
import { SelectedState, StateSelector } from './stateselector';
import { MatrixInput } from './matrixinput';
import { QuantumStateInput } from './quantumstateinput';
import { min, pi } from 'mathjs';
import { parse2DMatrix } from './utils';
import { isArray, min, pi } from 'mathjs';

// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
Expand Down Expand Up @@ -149,26 +150,57 @@ export function init(
qubitGateOpFormula();
});

const matrixInput = new MatrixInput(matrixContainer, (matrix: Matrix2x2) =>
setMatrixOnBloch(matrix)
);
new GateSelector(matrixContainer, (option: string) => {
const optionToMatrix: {
[key: string]: [string, [string, string], [string, string]];
} = {
[SelectedGate.X]: ['', ['0', '1'], ['1', '0']],
[SelectedGate.Y]: ['', ['0', '-i'], ['i', '0']],
[SelectedGate.Z]: ['', ['1', '0'], ['0', '-1']],
[SelectedGate.H]: ['sqrt(1/2)', ['1', '1'], ['1', '-1']],
[SelectedGate.Clear]: ['', ['', ''], ['', '']],
};
new GateSelector(matrixContainer, (option: string, onlyMatrix: boolean = false) => {
// const optionToMatrix: {
// [key: string]: [string, [string, string], [string, string]];
// // [key: string]: [string, [string, string], [string, string]] | string[];
// // [key: string]: [string, string];
// // [key: string]: string[];
// // [key: string]: any[];
// } = {
// [SelectedGate.X]: ['', ...parse2DMatrix('0 1 \\ 1 0')], // NOT, Bit-Flip
// [SelectedGate.Y]: ['', ...parse2DMatrix('0 -i \\ i 0')],
// [SelectedGate.Z]: ['', ...parse2DMatrix('1 0 \\ 0 -1')], // = T^4 Phase-Flip
// [SelectedGate.H]: ['sqrt(1/2)', ...parse2DMatrix('1 1 \\ 1 -1')], // Hadamard

// [SelectedGate.RX]: ['sqrt(1/2)', ...parse2DMatrix('1 -i \\ -i 1')], // θ=π/2 = exp(-i(θ/2)X)
// [SelectedGate.RY]: ['sqrt(1/2)', ...parse2DMatrix('1 -1 \\ 1 1')], // θ=π/2 = exp(-i(θ/2)Y)
// // [SelectedGate.RXdg]: ['sqrt(1/2)', ...parse2DMatrix('1 i \\ i 1')],
// // [SelectedGate.RYdg]: ['sqrt(1/2)', ...parse2DMatrix('1 1 \\ -1 1')],

// [SelectedGate.SX]: ['sqrt(1/2)', ...parse2DMatrix('1+i 1-i \\ 1-i 1+i')], // Sqrt of X
// // [SelectedGate.SXdg]: ['sqrt(1/2)', ...parse2DMatrix('1-i 1+i \\ 1+i 1-i')],
// // [SelectedGate.T]: ['', ...parse2DMatrix('1 0 \\ 0 exp(i*pi/4)')], // pi/8 // Equivalent to a π/4 radian rotation about the Z axis.
// // [SelectedGate.Tdg]: ['', ...parse2DMatrix('1 0 \\ 0 exp(-i*pi/4)')], // Equivalent to a -π/4 radian rotation about the Z axis.
// [SelectedGate.S]: ['', ...parse2DMatrix('1 0 \\ 0 i')], // = T^2 Phase // Equivalent to π/2 radian rotation about the Z axis.
// // [SelectedGate.Sdg]: ['', ...parse2DMatrix('1 0 \\ 0 -i')], // Singleton // Equivalent to -π/2 radian rotation about the Z axis.

// // [SelectedGate.P]: ['', ...parse2DMatrix('1 0 \\ 0 exp(i*λ)')], // λ=π=>Z λ=π/2=>S λ=π/4=>T Phase-Shift
// // [SelectedGate.I]: ['', ...parse2DMatrix('1 0 \\ 0 1')], // Identity

// [SelectedGate.Clear]: ['', ['', ''], ['', '']],
// };
// // const optionToMatrix: {
// // [key: string]: [string, [string, string], [string, string]];
// // } = Object.fromEntries(Object.entries(optionToMatrix_).map(([key, value]) => {
// // if (isArray(value[1])) {return [key, value as [string, [string, string], [string, string]]];}
// // return [key, [value[0], ...parse2DMatrix(value[1])]];
// // }));
const optionToMatrix = gate2Matrix;

if (onlyMatrix) {
return optionToMatrix[option];
}

matrixInput.setMatrix(optionToMatrix[option]);
setMatrixOnBloch(matrixInput.getMatrix());
// TODO: trigger Gate * qubit multiplication
qubitGateOpFormula();
});
const matrixInput = new MatrixInput(matrixContainer, (matrix: Matrix2x2) =>
setMatrixOnBloch(matrix)
);

const setMatrixOnBloch = (matrix: Matrix2x2 | null) => {
qubitGateOpFormula();
if (matrix === null) {
bloch.hideRotationAxis();
return;
Expand Down
55 changes: 34 additions & 21 deletions src/matrixinput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SVG } from 'mathjax-full/js/output/svg';
import { AllPackages } from 'mathjax-full/js/input/tex/AllPackages';
import { browserAdaptor } from 'mathjax-full/js/adaptors/browserAdaptor';
import { RegisterHTMLHandler } from 'mathjax-full/js/handlers/html';
import { Matrix2Latex } from './utils';
RegisterHTMLHandler(browserAdaptor());

type OnChangeCallback = (matrix: Matrix2x2) => void;
Expand Down Expand Up @@ -50,6 +51,33 @@ export class MatrixInput {

[this.uScalar, this.u00, this.u01, this.u10, this.u11].forEach((input) => {
input.addEventListener('change', this.onInputChange); // TODO: cleanup
// // Add arrow key support for quantum gate input
// input.addEventListener('keydown', function (event) {
// let isComplex = this.value.includes('i');
// let number = parseFloat(this.value.replace('i', ''));

// switch (event.key) {
// case 'ArrowUp':
// number += 0.1;
// if (number > 1 || number < -1) {
// number = number - 0.1;
// isComplex = !isComplex;
// }
// this.value = `${number.toFixed(1)}${isComplex ? 'i' : ''}`;
// event.preventDefault();
// break;
// case 'ArrowDown':
// number -= 0.1;
// if (number > 1 || number < -1) {
// number = number + 0.1;
// isComplex = !isComplex;
// }
// this.value = `${number.toFixed(1)}${isComplex ? 'i' : ''}`;
// event.preventDefault();
// break;
// }
// this.dispatchEvent(new Event('change'));
// });
});

// this.texField = this.container.querySelector(`[id="qubitGateOpFormula"]`);
Expand Down Expand Up @@ -82,12 +110,12 @@ export class MatrixInput {
this.u11.value = matrix[2][1];
}

getMatrix(): Matrix2x2 | null {
getMatrix(withoutScalar: boolean = false): Matrix2x2 | null {
// if (this.uScalar.value === null) return null;
// if ([this.uScalar.value, this.u00.value, this.u01.value, this.u10.value, this.u11.value].includes(null)) return null;
if ([this.uScalar.value, this.u00.value, this.u01.value, this.u10.value, this.u11.value].every((e) => e === '')) return null;

const scalar = evaluate(this.uScalar.value || '1'); // if scalar is empty multiply with 1
const scalar = withoutScalar ? 1 : evaluate(this.uScalar.value || '1'); // if scalar is empty multiply with 1
const matrix = [
// [evaluate(this.u00.value) * scalar, evaluate(this.u01.value) * scalar],
// [evaluate(this.u10.value) * scalar, evaluate(this.u11.value) * scalar],
Expand Down Expand Up @@ -125,28 +153,13 @@ export class MatrixInput {
// this.texField.innerHTML = String(res);
this.texField.innerHTML = `
$$
\\left (
\\begin{matrix}
${gate[0][0].toString()} & ${gate[0][1].toString()} \\\\
${gate[1][0].toString()} & ${gate[1][1].toString()}
\\end{matrix}
\\right )
\\left (
\\begin{matrix}
${math.round(stateMatrix[0], 2).toString()} \\\\
${math.round(stateMatrix[1], 2).toString()}
\\end{matrix}
\\right )
${Matrix2Latex(this.getMatrix(true), this.uScalar.value, false)}
${Matrix2Latex(math.round(math.transpose([stateMatrix]), 2), undefined, false)}
=
\\left (
\\begin{matrix}
${math.round(res[0], 2).toString()} \\\\
${math.round(res[1], 2).toString()}
\\end{matrix}
\\right )
${Matrix2Latex(math.round(math.transpose([res as number[]]), 2), undefined, false)}
$$
`;
this.GateQubitFormulaHTML.render().reset();
Expand Down
12 changes: 6 additions & 6 deletions src/stateselector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,28 @@ export class StateSelector extends ButtonSelector<SelectedState> {
key: SelectedState.state0,
// value: '|0⟩',
// value: '$$\\left | 0 \\right \\rangle$$',
value: ketStr('0'),
value: ketStr('0', true, [1, 0]),
},
{
key: SelectedState.state1,
// value: '|1⟩',
value: ketStr('1'),
value: ketStr('1', true, [0, 1]),
},
{
key: SelectedState.statePlus,
value: ketStr('+'),
value: ketStr('+', true, '\\frac{1}{\\sqrt{2}} \\begin{bmatrix} 1 \\\\ 1 \\end{bmatrix}'),
},
{
key: SelectedState.stateMinus,
value: ketStr('-'),
value: ketStr('-', true, '\\frac{1}{\\sqrt{2}} \\begin{bmatrix} 1 \\\\ -1 \\end{bmatrix}'),
},
{
key: SelectedState.stateI,
value: ketStr('i'),
value: ketStr('i', true, '\\frac{1}{\\sqrt{2}} \\begin{bmatrix} 1 \\\\ i \\end{bmatrix}'),
},
{
key: SelectedState.stateMinusI,
value: ketStr('-i'),
value: ketStr('-i', true, '\\frac{1}{\\sqrt{2}} \\begin{bmatrix} 1 \\\\ -i \\end{bmatrix}'),
},
]);
}
Expand Down
5 changes: 5 additions & 0 deletions src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ body {
margin-top: 6px;
}

button > mjx-container {
margin-top: unset !important;
margin-bottom: unset !important;
}

#matrixContainer input {
// width: 145px;
width: 100px;
Expand Down
Loading

0 comments on commit 252f436

Please sign in to comment.