From d35fe72574e348a58070710485109397b3fdc506 Mon Sep 17 00:00:00 2001 From: Utkarsh-Raj20 Date: Sat, 8 Mar 2025 19:00:26 +0530 Subject: [PATCH 1/2] ADD: export the gates in JSON --- .../src/components/QuantumCircuit.jsx | 102 ++++++------ .../src/components/circuitDataExtractor.jsx | 156 ++++++++++++++++++ .../visualizer/src/components/ui/button.jsx | 29 ++++ .../visualizer/src/components/ui/dialog.jsx | 33 ++++ qubitverse/visualizer/vite.config.js | 13 +- 5 files changed, 280 insertions(+), 53 deletions(-) create mode 100644 qubitverse/visualizer/src/components/circuitDataExtractor.jsx create mode 100644 qubitverse/visualizer/src/components/ui/button.jsx create mode 100644 qubitverse/visualizer/src/components/ui/dialog.jsx diff --git a/qubitverse/visualizer/src/components/QuantumCircuit.jsx b/qubitverse/visualizer/src/components/QuantumCircuit.jsx index 69b4ccc..5b633a5 100644 --- a/qubitverse/visualizer/src/components/QuantumCircuit.jsx +++ b/qubitverse/visualizer/src/components/QuantumCircuit.jsx @@ -1,14 +1,15 @@ import React, { useState, useRef, useEffect } from "react"; import { Stage, Layer, Line, Rect, Text, Group, Circle } from "react-konva"; import { MathJax, MathJaxContext } from "better-react-mathjax"; +import CircuitDataExtractor from "./circuitDataExtractor"; // // CONFIG CONSTANTS // const qubitSpacing = 50; // vertical spacing between qubit lines -const numQubits = 5; // Q0 through Q4 -const gateSize = 40; // width/height for single-qubit gate squares -const canvasMinX = 50; // left bound for gates on the stage +const numQubits = 5; // Q0 through Q4 +const gateSize = 40; // width/height for single-qubit gate squares +const canvasMinX = 50; // left bound for gates on the stage const canvasMaxX = 750 - gateSize; // right bound so gate stays visible // @@ -20,22 +21,24 @@ const gatesList = ["X", "Y", "H", "CNOT"]; // Tooltip content for each gate // const gateTooltips = { - "X": { + X: { desc: "Pauli-X (NOT) Gate", - latex: "$$X = \\begin{pmatrix}0 & 1\\\\ 1 & 0\\end{pmatrix}$$" + latex: "$$X = \\begin{pmatrix}0 & 1\\\\ 1 & 0\\end{pmatrix}$$", }, - "Y": { + Y: { desc: "Pauli-Y Gate", - latex: "$$Y = \\begin{pmatrix}0 & -i\\\\ i & 0\\end{pmatrix}$$" + latex: "$$Y = \\begin{pmatrix}0 & -i\\\\ i & 0\\end{pmatrix}$$", }, - "H": { + H: { desc: "Hadamard (Superposition) Gate", - latex: "$$H = \\frac{1}{\\sqrt{2}} \\begin{pmatrix}1 & 1\\\\ 1 & -1\\end{pmatrix}$$" + latex: + "$$H = \\frac{1}{\\sqrt{2}} \\begin{pmatrix}1 & 1\\\\ 1 & -1\\end{pmatrix}$$", }, - "CNOT": { + CNOT: { desc: "Controlled-NOT Gate", - latex: "$$CNOT = \\begin{pmatrix}1 & 0 & 0 & 0\\\\ 0 & 1 & 0 & 0\\\\ 0 & 0 & 0 & 1\\\\ 0 & 0 & 1 & 0\\end{pmatrix}$$" - } + latex: + "$$CNOT = \\begin{pmatrix}1 & 0 & 0 & 0\\\\ 0 & 1 & 0 & 0\\\\ 0 & 0 & 0 & 1\\\\ 0 & 0 & 1 & 0\\end{pmatrix}$$", + }, }; // @@ -54,7 +57,7 @@ const QuantumGate = ({ onDragEnd, fixedY, onRightClick, - order + order, }) => ( ({ x: clamp(pos.x, canvasMinX, canvasMaxX), - y: fixedY + y: fixedY, }) : undefined } @@ -103,14 +106,7 @@ const QuantumGate = ({ // // MULTI-QUBIT GATE: CNOT // -const CNOTGate = ({ - x, - control, - target, - onDragEnd, - onRightClick, - order -}) => { +const CNOTGate = ({ x, control, target, onDragEnd, onRightClick, order }) => { const yControl = (control + 1) * qubitSpacing; const yTarget = (target + 1) * qubitSpacing; return ( @@ -120,7 +116,7 @@ const CNOTGate = ({ y={0} dragBoundFunc={(pos) => ({ x: clamp(pos.x, canvasMinX, canvasMaxX), - y: 0 + y: 0, })} onDragEnd={onDragEnd} onContextMenu={(e) => { @@ -128,21 +124,15 @@ const CNOTGate = ({ onRightClick(); }} > - + - + + - - {order !== undefined && ( { // const QuantumCircuit = () => { // Separate arrays for single-qubit and CNOT gates. - const [gates, setGates] = useState([]); // { x, y, text } + const [gates, setGates] = useState([]); // { x, y, text } const [cnotGates, setCnotGates] = useState([]); // { x, control, target } const [selectedQubit, setSelectedQubit] = useState(null); // for Bloch sphere popup const [modalOpen, setModalOpen] = useState(false); @@ -208,7 +198,7 @@ const QuantumCircuit = () => { x: 0, y: 0, desc: "", - latex: "" + latex: "", }); const stageRef = useRef(null); @@ -262,7 +252,7 @@ const QuantumCircuit = () => { const snappedY = snapY(pointerY); setGates((prev) => [ ...prev, - { x: pointerX, y: snappedY, text: gateType } + { x: pointerX, y: snappedY, text: gateType }, ]); } }; @@ -280,7 +270,10 @@ const QuantumCircuit = () => { const { x } = e.target.position(); setGates((prev) => { const newGates = [...prev]; - newGates[index] = { ...newGates[index], x: clamp(x, canvasMinX, canvasMaxX) }; + newGates[index] = { + ...newGates[index], + x: clamp(x, canvasMinX, canvasMaxX), + }; return newGates; }); }; @@ -323,7 +316,7 @@ const QuantumCircuit = () => { } setCnotGates((prev) => [ ...prev, - { x: cnotX, control: cnotControl, target: cnotTarget } + { x: cnotX, control: cnotControl, target: cnotTarget }, ]); setCnotModalOpen(false); }; @@ -339,7 +332,7 @@ const QuantumCircuit = () => { x: rect.left, y: rect.bottom + 5, desc: gateTooltips[gate].desc, - latex: gateTooltips[gate].latex + latex: gateTooltips[gate].latex, }); }; const handleTooltipLeave = () => { @@ -348,7 +341,14 @@ const QuantumCircuit = () => { return ( -
+
{/* TOP MENU */}
{ border: "2px solid black", borderRadius: "5px", background: "white", - marginBottom: "10px" + marginBottom: "10px", }} > {gatesList.map((gate, index) => ( @@ -373,7 +373,7 @@ const QuantumCircuit = () => { margin: "5px", borderRadius: "5px", cursor: "grab", - background: "white" + background: "white", }} draggable onDragStart={(e) => e.dataTransfer.setData("text/plain", gate)} @@ -397,7 +397,7 @@ const QuantumCircuit = () => { border: "1px solid #ccc", borderRadius: "4px", zIndex: 1000, - boxShadow: "0px 0px 5px rgba(0,0,0,0.3)" + boxShadow: "0px 0px 5px rgba(0,0,0,0.3)", }} >
@@ -465,7 +465,7 @@ const QuantumCircuit = () => { background: "rgba(0, 0, 0, 0.5)", display: "flex", alignItems: "center", - justifyContent: "center" + justifyContent: "center", }} >
{ background: "white", padding: "20px", borderRadius: "10px", - textAlign: "center" + textAlign: "center", }} >

Bloch Sphere for Q{selectedQubit}

@@ -504,7 +504,7 @@ const QuantumCircuit = () => { background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", - justifyContent: "center" + justifyContent: "center", }} >
{ padding: "20px", borderRadius: "10px", textAlign: "center", - minWidth: "300px" + minWidth: "300px", }} >

Select Control and Target Qubit

@@ -544,7 +544,10 @@ const QuantumCircuit = () => { ))}
- @@ -552,6 +555,7 @@ const QuantumCircuit = () => {
)}
+ ); }; diff --git a/qubitverse/visualizer/src/components/circuitDataExtractor.jsx b/qubitverse/visualizer/src/components/circuitDataExtractor.jsx new file mode 100644 index 0000000..1c70f62 --- /dev/null +++ b/qubitverse/visualizer/src/components/circuitDataExtractor.jsx @@ -0,0 +1,156 @@ +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; +import { Code } from "lucide-react"; + +// This function extracts circuit data from the gates state +export function extractCircuitData(gates, cnotGates) { + // Process single-qubit gates + const processedGates = gates.map((gate) => { + // Calculate which qubit this gate is on based on y position + const qubitIndex = Math.round((gate.y + 20) / 50) - 1; // 20 is half of gateSize, 50 is qubitSpacing + + return { + type: "single", + gateType: gate.text, // X, Y, H, etc. + qubit: qubitIndex, + position: gate.x, + }; + }); + + // Process CNOT gates + const processedCnotGates = cnotGates.map((gate) => { + return { + type: "cnot", + control: gate.control, + target: gate.target, + position: gate.x, + }; + }); + + // Combine all gates and sort by position (x coordinate) + const allGates = [...processedGates, ...processedCnotGates].sort( + (a, b) => a.position - b.position + ); + + // Create a map of gates by qubit + const gatesByQubit = {}; + for (let i = 0; i < 5; i++) { + // numQubits = 5 + gatesByQubit[`Q${i}`] = allGates + .filter( + (gate) => + (gate.type === "single" && gate.qubit === i) || + (gate.type === "cnot" && (gate.control === i || gate.target === i)) + ) + .sort((a, b) => a.position - b.position); + } + + // Create the final circuit data object + return { + numQubits: 5, + gates: allGates, + gatesByQubit, + // Add execution order for each qubit + executionOrder: Object.keys(gatesByQubit).reduce((acc, qubit) => { + acc[qubit] = gatesByQubit[qubit].map((gate) => { + if (gate.type === "single") { + return { + operation: gate.gateType, + qubit: gate.qubit, + position: gate.position, + }; + } else { + return { + operation: "CNOT", + control: gate.control, + target: gate.target, + position: gate.position, + }; + } + }); + return acc; + }, {}), + }; +} + +export default function CircuitDataExtractor({ gates, cnotGates }) { + const [isOpen, setIsOpen] = useState(false); + const [circuitData, setCircuitData] = useState(null); + + const handleExtractData = () => { + const data = extractCircuitData(gates, cnotGates); + setCircuitData(data); + setIsOpen(true); + }; + + const handleCopyToClipboard = () => { + if (circuitData) { + navigator.clipboard + .writeText(JSON.stringify(circuitData, null, 2)) + .then(() => { + alert("Circuit data copied to clipboard!"); + }) + .catch((err) => { + console.error("Failed to copy: ", err); + }); + } + }; + + const handleDownloadJSON = () => { + if (circuitData) { + const dataStr = JSON.stringify(circuitData, null, 2); + const dataUri = + "data:application/json;charset=utf-8," + encodeURIComponent(dataStr); + + const downloadLink = document.createElement("a"); + downloadLink.setAttribute("href", dataUri); + downloadLink.setAttribute("download", "quantum-circuit.json"); + document.body.appendChild(downloadLink); + downloadLink.click(); + document.body.removeChild(downloadLink); + } + }; + + return ( + <> + + + + + + Quantum Circuit Data (JSON) + + This is the extracted data representation of your quantum circuit + + + +
+
+              {circuitData ? JSON.stringify(circuitData, null, 2) : ""}
+            
+
+ +
+ + +
+
+
+ + ); +} diff --git a/qubitverse/visualizer/src/components/ui/button.jsx b/qubitverse/visualizer/src/components/ui/button.jsx new file mode 100644 index 0000000..ec9064f --- /dev/null +++ b/qubitverse/visualizer/src/components/ui/button.jsx @@ -0,0 +1,29 @@ +import React from "react"; + +export const Button = ({ + variant = "default", + onClick, + children, + className = "", +}) => { + // Define button styles based on the variant + const baseStyles = + "px-4 py-2 rounded-md font-semibold text-sm transition-all duration-200 ease-in-out"; + const variantStyles = { + default: "bg-blue-500 text-white hover:bg-blue-600", + outline: + "border-2 border-blue-500 text-blue-500 hover:bg-blue-500 hover:text-white", + // Add more variants as needed + }; + + return ( + + ); +}; diff --git a/qubitverse/visualizer/src/components/ui/dialog.jsx b/qubitverse/visualizer/src/components/ui/dialog.jsx new file mode 100644 index 0000000..d33ac8f --- /dev/null +++ b/qubitverse/visualizer/src/components/ui/dialog.jsx @@ -0,0 +1,33 @@ +import React from "react"; + +export const Dialog = ({ open, onOpenChange, children }) => { + return ( + open && ( +
+
+ + {children} +
+
+ ) + ); +}; + +export const DialogContent = ({ children }) =>
{children}
; + +export const DialogHeader = ({ children }) => ( +
{children}
+); + +export const DialogTitle = ({ children }) => ( +
{children}
+); + +export const DialogDescription = ({ children }) => ( +
{children}
+); diff --git a/qubitverse/visualizer/vite.config.js b/qubitverse/visualizer/vite.config.js index 8b0f57b..10b1241 100644 --- a/qubitverse/visualizer/vite.config.js +++ b/qubitverse/visualizer/vite.config.js @@ -1,7 +1,12 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; -// https://vite.dev/config/ export default defineConfig({ plugins: [react()], -}) + resolve: { + alias: { + '@': path.resolve(__dirname, 'src'), // Define '@' as an alias for 'src' + }, + }, +}); From 25b612bb17f7e33a9926c8653b432fa57ebd6cf0 Mon Sep 17 00:00:00 2001 From: Utkarsh-Raj20 Date: Sat, 8 Mar 2025 19:31:24 +0530 Subject: [PATCH 2/2] UPDATE: Added I,Z,S and T gates to ui --- .../src/components/QuantumCircuit.jsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/qubitverse/visualizer/src/components/QuantumCircuit.jsx b/qubitverse/visualizer/src/components/QuantumCircuit.jsx index 5b633a5..b5e24a5 100644 --- a/qubitverse/visualizer/src/components/QuantumCircuit.jsx +++ b/qubitverse/visualizer/src/components/QuantumCircuit.jsx @@ -15,12 +15,16 @@ const canvasMaxX = 750 - gateSize; // right bound so gate stays visible // // Gate types for the top menu // -const gatesList = ["X", "Y", "H", "CNOT"]; +const gatesList = ["I", "X", "Y", "Z", "H", "S", "T", "CNOT"]; // // Tooltip content for each gate // const gateTooltips = { + I: { + desc: "Identity Gate", + latex: "$$I = \\begin{pmatrix} 1 & 0 \\\\ 0 & 1 \\end{pmatrix}$$", + }, X: { desc: "Pauli-X (NOT) Gate", latex: "$$X = \\begin{pmatrix}0 & 1\\\\ 1 & 0\\end{pmatrix}$$", @@ -29,6 +33,18 @@ const gateTooltips = { desc: "Pauli-Y Gate", latex: "$$Y = \\begin{pmatrix}0 & -i\\\\ i & 0\\end{pmatrix}$$", }, + Z: { + desc: "Pauli-Z Gate", + latex: "$$Z = \\begin{pmatrix} 1 & 0 \\\\ 0 & -1 \\end{pmatrix}$$", + }, + S: { + desc: "Phase π/2 Shift", + latex: "$$S = \\begin{pmatrix} 1 & 0 \\\\ 0 & i \\end{pmatrix}$$", + }, + T: { + desc: "Phase π/4 Shift", + latex: "$$T = \\begin{pmatrix} 1 & 0 \\\\ 0 & e^{iπ/4} \\end{pmatrix}$$", + }, H: { desc: "Hadamard (Superposition) Gate", latex: