Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quantum js cli #34

Open
wants to merge 32 commits into
base: ltnln-npm-package
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
40ae959
Create quantum-js-cli package
Aug 5, 2021
e66857c
Create tests for quantum-js-cli runner.js
Aug 5, 2021
9fa52c3
Update HTML files to reference bundled Qjs
Aug 11, 2021
788727e
Set up build script for bundling css files
Aug 11, 2021
e7de83d
Update dispatchCustomEventToGlobal method and references in other files
Aug 11, 2021
105614d
Change entry point for quantum-js-util package
Aug 11, 2021
9692c33
Create quantum-js-vis visualization package for the GUI
Aug 11, 2021
931f168
Adjust help() calls to use logger module
Aug 11, 2021
54dd31f
Create bundles
Aug 11, 2021
b3f92a8
Merge pull request #1 from Altanali/quantum-js-vis-branch
Altanali Aug 11, 2021
c36ecd9
Update gate to not create default amazonBraketSymbols for gates.
Aug 11, 2021
f6f5551
Merge pull request #2 from Altanali/quantum-js-vis-branch
Altanali Aug 11, 2021
1eb9203
Update bundle.js, no longer creates default AmazonBraketSymbols for Qjs
Aug 11, 2021
e98a7af
Merge pull request #3 from Altanali/quantum-js-vis-branch
Altanali Aug 11, 2021
19eff83
Reduce width of palette on creation in a JupyterNotebook via braket()…
Aug 11, 2021
50c6ea4
Merge pull request #4 from Altanali/quantum-js-vis-branch
Altanali Aug 11, 2021
c3c08fa
Reduce width of palette on creation in a JupyterNotebook via braket()…
Aug 11, 2021
059d221
Update eslint scripts in root and quantum-js-vis package
Aug 12, 2021
26ad302
Correct circuit.clone() variable names and add gate.name and gate.css…
Aug 13, 2021
4e46ec2
Add Gate.findByNameCss function to Gate class
Aug 13, 2021
0a4aec8
RClarify regex usage, user input prompts, and correct circuitBackup g…
Aug 13, 2021
fd302f3
Change prepareCircuit() prompt to clarify that a Number is wanted by …
Aug 13, 2021
49b52cd
Revert Q.css to original Q.css to remove unicode discrepancies. Updat…
Aug 13, 2021
1c99533
Merge pull request #5 from Altanali/quantum-js-vis-branch
Altanali Aug 13, 2021
21ef0ab
Merge pull request #6 from Altanali/quantum-js-vis-branch
Altanali Aug 13, 2021
ade84d7
Merge pull request #7 from Altanali/quantum-js-cli
Altanali Aug 13, 2021
cc37d84
Update set10754() when referred to by gate names/symbols to include p…
Aug 13, 2021
afc8355
Make prompt an argument for testability and flexible i/o.
Aug 14, 2021
2547699
Add tests for prepareCircuit(), only from scratch
Aug 14, 2021
aff2a77
Change quantum-js-util entry point to index.js
Aug 14, 2021
374e395
remove extraneous console.log
Aug 14, 2021
5608587
packages/quantum-js-cli/runner.js
Aug 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/quantum-js-cli/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const {run} = require('./runner');

run();
15 changes: 15 additions & 0 deletions packages/quantum-js-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "quantum-js-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
"dependencies": {
"prompt-sync": "^4.2.0",
"readline-sync": "^1.4.10"
},
"author": "",
"license": "ISC"
}
222 changes: 222 additions & 0 deletions packages/quantum-js-cli/runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
const {Q, Circuit, Gate, logger} = require('quantum-js-util');
const prompt = require('prompt-sync')({sigint: true});
var readlineSync = require('readline-sync');
const mark = "> ";


function prepareCircuit() {
let selection = NaN;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason it starts with NaN and not null/0 etc

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I only wanted to instantiate selection as some Falsey value.

console.clear();
let circuit;
while(!selection) {
console.log("Please select a method to begin circuit creation: ");
console.log("1. From Scratch\n" +
"2. From Text Diagram\n"
/*+ "3. From Table Transposed\n"*/);
selection = +prompt(mark);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afai understand this is converting the prompt result into a number ? This could be more explicit, it's not quickly obvious what that syntax does

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can use the Number() cast instead of +() cast to Number to clarity instead.

switch(selection) {
case 1:
let num_registers = NaN;
while(!num_registers) {
console.log("Enter the number of qubits you would like to start out with.\n");
num_registers = +prompt(mark);
}
circuit = Q(num_registers, 8);
break;
case 2:
circuit = prepareCircuitFromTable();
break;
default:
selection = NaN
}
}
if(!(circuit instanceof Circuit)) {
logger.error("Failed to create circuit");
process.exit();
}
console.log(circuit.toDiagram());
return circuit;
}

function prepareCircuitFromTable() {
let resultingCircuit;
let tableString;
let lines = [];
console.log('Input (or paste) your table below and press [Enter] key twice to submit.');
readlineSync.promptLoop(line => {
lines.push(line);
return !line;
}, {prompt: ''});
tableString = lines.join('\n');
try {
resultingCircuit = Q(tableString);
}
catch(e) {
return logger.error("Failed to create circuit from table.");
}
return resultingCircuit;
}



function printMenu() {
let menu =
`-h, help Print Q.js command line options (currently set)
toDiagram Print the current circuit as a text diagram
toAmazonBraket Export the current circuit as Python code using the Amazon Braket SDK.
toLaTeX Print the current circuit as a LaTeX diagram
toText Print as a table using only common characters (can be used to import later).
report Evaluate and current circuit's probability report
clear Clear the console
newCircuit Discard the current circuit and create a new circuit.
q, quit Exit the command line
`;
console.log(menu);
return menu;
}

function evaluateOperation(input, circuit) {
let functionName = (/[^\s,\[\]()]+/g).exec(input)[0];
let gate = Gate.findBySymbol(functionName);
if(!gate) gate = Gate.findByNameCss(functionName)
//checks that the function call is gate set$ operation and not another circuit operation.
//Syntax: GateSymbol(momentIndex, [registerIndex0, registerIndex1,...])
//Regex explanation: looks for the first INTEGER of the from "(digit0digit1digit2..." and removes the "(" at the beginning.
let momentIndex = +(/\(\s*\d+/).exec(input)[0].substring(1);
if(momentIndex === undefined) {
return logger.error("Invalid gate set operation syntax");
}
//
let registerIndices;
let re = /\[(\s*\d+\s*,{0,1}\s*)+\]/g;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this regex for?

Copy link
Collaborator Author

@Altanali Altanali Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a regex that selects an array of integers from a string, i.e. any substring of the form "[integer1, integer2, integer3...]". I'll make the clear in a comment in the code.

try {
registerIndices = (re)
.exec(input)[0]
.slice(1, -1)
.split(',')
.map(index => +index);
}
catch(e) {
return logger.error("Invalid gate set operation syntax");
}
let newParameters = {};
input = input.substring(re.lastIndex);
re = /\d+\.{0,1}\d*/g
let input_params = [];
while(value = re.exec(input)) {
input_params.push(+value[0]);
}
input_params.reverse();
if(gate.has_parameters) {
if(input_params.length > Object.keys(gate.parameters).length) return logger.error("b Invalid gate set operation syntax");
Object.keys(gate.parameters).forEach(key => {
newParameters[key] = input_params.pop();
if(!newParameters[key]) {
newParameters[key] = gate.parameters[key];
}
});
}
else if(input_params.length !== 0) return logger.error("c Invalid gate set operation syntax");
return circuit[functionName](momentIndex, registerIndices, newParameters);
}


function removeOperation(input, circuit) {
let momentIndex = +(/\(\s*\d+/).exec(input)[0].substring(1);
if(momentIndex === undefined) {
return logger.error("Invalid gate set operation syntax");
}
//
let registerIndices;
let re = /\[(\s*\d+\s*,{0,1}\s*)+\]/g;
try {
registerIndices = (re)
.exec(input)[0]
.slice(1, -1)
.split(',')
.map(index => +index);
}
catch(e) {
return logger.error("Invalid gate set operation syntax");
}
if(input.substring(re.lastIndex).trim() != ")") {
return logger.error("Invalid gate set operation syntax");
}
let operationToRemove = circuit.get(momentIndex, registerIndices[0]);
if(!operationToRemove) {
return logger.log("No operation to remove");
}
if(registerIndices.every(index => {
return operationToRemove.registerIndices.includes(index);
})) return circuit.clear$(momentIndex, operationToRemove.registerIndices);
}

function evaluateInput(input, circuit) {
switch(input) {
case "-h":
case "help":
printMenu();
break;
case "toDiagram":
console.log(circuit.toDiagram());
break;
case "toAmazonBraket":
console.log(circuit.toAmazonBraket());
break;
case "toLaTeX":
console.log(circuit.toLatex());
break;
case "report":
circuit.evaluate$();
console.log(circuit.report$());
break;
case "clear":
console.clear();
break;
case "toText":
console.log(circuit.toText(true));
break;
case "newCircuit":
let response = prompt("Creating a new circuit will discard the current circuit. Enter yes to continue: ").toLowerCase();
if(response !== "yes") console.log("Did not create new circuit.");
else circuit = prepareCircuit();
break;
default:
let circuitBackup = circuit.clone();
let re = /((\w+-*)+\(\s*\d+\s*,\s*\[(\s*\d+\s*,{0,1}\s*)+\]\s*(,\s*\d+\.{0,1}\d*\s*)*\))/g;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs better naming

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK. Will update.

while(functionCall = re.exec(input)) {
functionCall = functionCall[0];
let functionName = (/[^\s,\[\]()]+/g).exec(functionCall)[0];
//checks that the function call is gate set$ operation and not another circuit operation.
//Syntax: GateSymbol(momentIndex, [registerIndex0, registerIndex1,...])
if(circuit[functionName] instanceof Function && (Gate.findBySymbol(functionName) instanceof Gate || Gate.findByNameCss(functionName) instanceof Gate)) {
if(evaluateOperation(functionCall, circuit) === "(error)") {
circuit = circuitBackup;
break;
}
}
//Syntax: clear(momentIndex, registerIndex)
//If the registerIndex is the index of a multiqubit operation, we clear all indices associated with the operation under registerIndex
else if(functionName == "remove") {
if(removeOperation(functionCall, circuit) === "(error)") {
circuit = circuitBackup;
break;
}
}
}
}
return circuit;
}


function run() {
let circuit = prepareCircuit();
let input = prompt(mark);
while(input !== "quit" && input !== "q") {
evaluateInput(input, circuit);
input = prompt(mark)
}

}

module.exports = {run, evaluateInput, removeOperation, evaluateOperation, printMenu};