Skip to content

Commit

Permalink
User supported qpu selection (#135)
Browse files Browse the repository at this point in the history
* add checkbox

* add user task

* add form

* open new page in new tab to keep old page

* add result field

* rename result field for scripts

* set required attribute to complete task

* add selected provider field, change identifier

* add already_selected

* fix type

* display file in form

* remove field, fix submit

* update endpoint

* update manual hardware selection

* add nisq analyzer ui endpoint to config modal

* set attrs of user task

* update form, fix sequence flow, change layout

* add required attribute to provider

* hide button after successful request

* add readonly attribute
  • Loading branch information
LaviniaStiliadou authored Dec 18, 2023
1 parent c07c227 commit 33c4e03
Show file tree
Hide file tree
Showing 12 changed files with 505 additions and 185 deletions.
15 changes: 15 additions & 0 deletions components/bpmn-q/modeler-component/editor/util/IoUtilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {
import { getModeler } from "../ModelerHandler";
import { dispatchWorkflowEvent } from "../events/EditorEventHandler";
import fetch from "node-fetch";
import getHardwareSelectionForm from "../../extensions/quantme/replacement/hardware-selection/HardwareSelectionForm";
import JSZip from "jszip";

const editorConfig = require("../config/EditorConfigManager");
const quantmeConfig = require("../../extensions/quantme/framework-config/config-manager");

let FormData = require("form-data");

Expand Down Expand Up @@ -187,6 +189,19 @@ export async function deployWorkflowToCamunda(
form.append(key, viewBlob);
}

// add hardware selection form
const hardwareSelectionForm = getHardwareSelectionForm(
quantmeConfig.getNisqAnalyzerUiEndpoint(),
quantmeConfig.getNisqAnalyzerEndpoint(),
editorConfig.getCamundaEndpoint()
);
const hardwareSelectionFormFile = new File(
[hardwareSelectionForm],
"hardwareSelection.html",
{ type: "text/html" }
);
form.append("deployment", hardwareSelectionFormFile);

// make the request and wait for the response of the deployment endpoint
try {
const response = await fetch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const CUTTING_METHOD = "cuttingMethod";
export const MAX_SUBCIRCUIT_WIDTH = "maxSubCircuitWidth";
export const MAX_NUMBER_OF_CUTS = "maxNumberOfCuts";
export const MAXIMUM_NUM_SUBCIRCUITS = "maxNumSubCircuits";
export const AUTOMATED_SELECTION = "automatedSelection";
export const ERROR_CORRECTION_METHOD = "errorCorrectionMethod";

export const EXECUTION_RESULT = "executionResult";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export default function QuantMETab() {
const [nisqAnalyzerEndpoint, setNisqAnalyzerEndpoint] = useState(
config.getNisqAnalyzerEndpoint()
);
const [nisqAnalyzerUiEndpoint, setNisqAnalyzerUiEndpoint] = useState(
config.getNisqAnalyzerUiEndpoint()
);
const [qprovEndpoint, setQProvEndpoint] = useState(config.getQProvEndpoint());
const [qiskitRuntimeHandlerEndpoint, setQiskitRuntimeHandlerEndpoint] =
useState(config.getQiskitRuntimeHandlerEndpoint());
Expand Down Expand Up @@ -80,6 +83,13 @@ export default function QuantMETab() {
},
});
}
if (!editorActions._actions.hasOwnProperty("nisqAnalyzerUiEndpointChanged")) {
editorActions.register({
nisqAnalyzerUiEndpointChanged: function (nisqAnalyzerUiEndpoint) {
self.modeler.config.nisqAnalyzerUiEndpoint = nisqAnalyzerUiEndpoint;
},
});
}
if (!editorActions._actions.hasOwnProperty("qprovEndpointChanged")) {
editorActions.register({
qprovEndpointChanged: function (qprovEndpoint) {
Expand Down Expand Up @@ -122,6 +132,7 @@ export default function QuantMETab() {
// save changed config entries on close
QuantMETab.prototype.onClose = () => {
modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint;
modeler.config.nisqAnalyzerUiEndpoint = nisqAnalyzerUiEndpoint;
modeler.config.transformationFrameworkEndpoint =
transformationFrameworkEndpoint;
modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint;
Expand All @@ -131,6 +142,7 @@ export default function QuantMETab() {
modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint;
modeler.config.qprovEndpoint = qprovEndpoint;
config.setNisqAnalyzerEndpoint(nisqAnalyzerEndpoint);
config.setNisqAnalyzerUiEndpoint(nisqAnalyzerUiEndpoint);
config.setTransformationFrameworkEndpoint(transformationFrameworkEndpoint);
config.setScriptSplitterEndpoint(scriptSplitterEndpoint);
config.setScriptSplitterThreshold(scriptSplitterThreshold);
Expand Down Expand Up @@ -178,6 +190,20 @@ export default function QuantMETab() {
/>
</td>
</tr>
<tr className="spaceUnder">
<td align="right">NISQ Analyzer UI Endpoint:</td>
<td align="left">
<input
className="qwm-input"
type="string"
name="nisqAnalyzerUiEndpoint"
value={nisqAnalyzerUiEndpoint}
onChange={(event) =>
setNisqAnalyzerUiEndpoint(event.target.value)
}
/>
</td>
</tr>
</tbody>
</table>
<h3>QProv</h3>
Expand Down Expand Up @@ -294,4 +320,5 @@ QuantMETab.prototype.config = () => {
modeler.config.scriptSplitterEndpoint = config.getScriptSplitterEndpoint();
modeler.config.scriptSplitterThreshold = config.getScriptSplitterThreshold();
modeler.config.qprovEndpoint = config.getQProvEndpoint();
modeler.config.nisqAnalyzerUiEndpoint = config.getNisqAnalyzerUiEndpoint();
};
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,25 @@ export function setNisqAnalyzerEndpoint(nisqAnalyzerEndpoint) {
}
}

/**
* Get the NISQ Analyzer UI Endpoint
*/
export function getNisqAnalyzerUiEndpoint() {
if (config.nisqAnalyzerUiEndpoint === undefined) {
setNisqAnalyzerUiEndpoint(defaultConfig.nisqAnalyzerUiEndpoint);
}
return config.nisqAnalyzerUiEndpoint;
}

/**
* Set the NISQ Analyzer UI Endpoint
*/
export function setNisqAnalyzerUiEndpoint(nisqAnalyzerUiEndpoint) {
if (nisqAnalyzerUiEndpoint !== null && nisqAnalyzerUiEndpoint !== undefined) {
config.nisqAnalyzerUiEndpoint = nisqAnalyzerUiEndpoint;
}
}

/**
* Get the QProv endpoint
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const defaultConfig = {
wineryEndpoint: process.env.WINERY_ENDPOINT,
camundaEndpoint: process.env.CAMUNDA_ENDPOINT,
nisqAnalyzerEndpoint: process.env.NISQ_ANALYZER_ENDPOINT,
nisqAnalyzerUiEndpoint: process.env.NISQ_ANALYZER_UI_ENDPOINT,
qprovEndpoint: process.env.QPROV_ENDPOINT,
githubToken: process.env.GITHUB_TOKEN,
transformationFrameworkEndpoint:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@

import React from "@bpmn-io/properties-panel/preact/compat";

import { TextFieldEntry, SelectEntry } from "@bpmn-io/properties-panel";
import {
TextFieldEntry,
SelectEntry,
CheckboxEntry,
} from "@bpmn-io/properties-panel";
import * as consts from "../../Constants";
import { useService } from "bpmn-js-properties-panel";
import { HiddenTextFieldEntry } from "../../../../editor/popup/HiddenTextFieldEntry";
Expand Down Expand Up @@ -482,6 +486,35 @@ export function SelectionStrategyEntry({ element }) {
);
}

export function AutomatedSelectionEntry({ element }) {
const modeling = useService("modeling");
const translate =
useService("translate") ||
function (str) {
return str;
};
const debounce = useService("debounceInput");

const getValue = function () {
return element.businessObject.automatedSelection;
};

const setValue = function (newValue) {
return modeling.updateProperties(element, {
automatedSelection: newValue,
});
};

return (
<CheckboxEntry
id={consts.AUTOMATED_SELECTION}
label={translate("Automated Selection")}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>
);
}
export function CalibrationMethodEntry({ element }) {
const modeling = useService("modeling");
const translate =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import {
AlgorithmEntry,
AlphaEntry,
AutomatedSelectionEntry,
CalibrationMethodEntry,
DNNHiddenLayersEntry,
EncodingSchemaEntry,
Expand Down Expand Up @@ -268,6 +269,12 @@ export function HardwareSelectionSubprocessProperties(element) {
component: SelectionStrategyEntry,
isEdited: isTextFieldEntryEdited,
},
{
id: consts.AUTOMATED_SELECTION,
element,
component: AutomatedSelectionEntry,
isEdited: isTextFieldEntryEdited,
},
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Copyright (c) 2023 Institute of Architecture of Application Systems -
* University of Stuttgart
*
* This program and the accompanying materials are made available under the
* terms the Apache Software License 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: Apache-2.0
*/
export default function getHardwareSelectionForm(
nisqAnalyzerUiEndpoint,
nisqAnalyzerEndpoint,
camundaEndpoint
) {
const implementationEndpoint =
nisqAnalyzerEndpoint + "/nisq-analyzer/implementations/";
let hardwareSelectionForm = `<form role="form" name="form">
<div class="form-group">
<label for="circuitUrl-field">Circuit URL</label>
<input cam-variable-name="circuitUrl" cam-variable-type="String" class="form-control" id="circuitUrl" readonly />
</div>
<div class="form-group" id="selectedProviderGroup" style="display:none">
<label for="selected_provider-field">Selected Provider</label>
<input required id="selected_provider" cam-variable-name="selected_provider" cam-variable-type="String" class="form-control" />
</div>
<div class="form-group" style="display:none">
<label for="circuit_language-field">Circuit Language</label>
<input id="circuit_language" cam-variable-name="circuit_language" cam-variable-type="String" class="form-control" />
</div>
<div class="form-group" style="display:none">
<label for="already_selected-field">Already selected</label>
<input id="already_selected" cam-variable-name="already_selected" cam-variable-type="Boolean" class="form-control" />
</div>
<div class="form-group" id="selectedQpuGroup" style="display:none">
<label for="selected_qpu-field">Selected QPU</label>
<input required id="selected_qpu" cam-variable-name="selected_qpu" cam-variable-type="String" class="form-control" />
</div>
<button type="button" class="btn btn-primary ng-binding" id="submitButton">Submit</button>
</form>
<script>
async function getVariables(circuitUrl) {
let circuitLanguageInput = document.getElementById('circuit_language');
circuitLanguageInput.value = "openqasm";
let alreadySelectedInput = document.getElementById('already_selected');
alreadySelectedInput.value = true;
console.log("Circuit Language:", circuitLanguageInput.value);
console.log("Already Selected:", alreadySelectedInput.value);
let implementedAlgorithm = generateUUID();
console.log("Generated Algorithm Id: ", implementedAlgorithm);
try {
let apiUrl = "${implementationEndpoint}";
console.log(apiUrl);
let xhr = new XMLHttpRequest();
xhr.open("POST", apiUrl, true);
xhr.setRequestHeader("Content-Type", "application/json");
let data = { "id": null, "algorithmName": "Hardware_Selection_" + implementedAlgorithm, "implementedAlgorithm": implementedAlgorithm, "name": "Manual_Hardware_Selection_" + implementedAlgorithm, "language": "OpenQASM", "sdk": "Qiskit", "fileLocation": circuitUrl, "selectionRule": "" };
let jsonData = JSON.stringify(data);
console.log(jsonData)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 201) {
console.log("created implementation");
let uiEndpoint = "${nisqAnalyzerUiEndpoint}";
console.log(uiEndpoint);
document.getElementById('selectedProviderGroup').style.display = 'block';
document.getElementById('selectedQpuGroup').style.display = 'block';
document.getElementById('submitButton').style.display = 'none';
window.open(uiEndpoint, "_blank");
}
}
}
xhr.send(jsonData);
} catch (error) {
console.error(error);
}
}
function generateUUID() {
let uuid = '';
let chars = '0123456789abcdef';
for (let i = 0; i < 36; i++) {
let randomNumber = (Math.random() * 16) | 0;
let char = chars[i === 14 ? 4 : (i === 19 ? (randomNumber & 0x3) | 0x8 : randomNumber)];
uuid += i === 8 || i === 13 || i === 18 || i === 23 ? '-' : char;
}
return uuid;
}
function extractAndCall() {
const camundaEndpoint = "${camundaEndpoint}";
const circuitUrl = camundaEndpoint + document.getElementById('circuitUrl').value;
console.log(circuitUrl);
// Call getVariables with the extracted value
getVariables(circuitUrl)
}
document.getElementById("submitButton").addEventListener("click", extractAndCall);
</script>
`;
return hardwareSelectionForm;
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,28 @@ try {
throw new org.camunda.bpm.engine.delegate.BpmnError("Unable to connect to given endpoint: " + transformationUrl);
}
`;

// script to access the process instance id and to convert the circuit to a file
export var CONVERT_CIRCUIT = `import org.camunda.bpm.engine.variable.value.FileValue
import org.camunda.bpm.engine.variable.Variables
def circuit = execution.getVariable("circuit");
if (circuit instanceof ArrayList) {
circuit = circuit.get(0);
}
def circuitUrl ="/process-instance/" + execution.getProcessInstanceId() + "/variables/quantum_circuit/data";
execution.setVariable("circuitUrl", circuitUrl);
if (circuit instanceof File) {
execution.setVariable("quantum_circuit", circuit);
} else{
def file = new File("fragment.tmp");
file.write(circuit);
FileValue typedFileValue = Variables
.fileValue("fragment.tmp")
.file(file)
.mimeType("text/plain")
.encoding("UTF-8")
.create();
execution.setVariable("quantum_circuit", typedFileValue);
}
`;
Loading

0 comments on commit 33c4e03

Please sign in to comment.