From 33c4e0364416e5f9e9c883f35666940aaea48109 Mon Sep 17 00:00:00 2001 From: LaviniaStiliadou Date: Mon, 18 Dec 2023 15:11:42 +0100 Subject: [PATCH] User supported qpu selection (#135) * 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 --- .../editor/util/IoUtilities.js | 15 + .../extensions/quantme/Constants.js | 1 + .../quantme/configTabs/QuantMETab.js | 27 ++ .../framework-config/config-manager.js | 19 + .../quantme/framework-config/config.js | 1 + .../QuantMEPropertyEntries.js | 35 +- .../QuantMETaskProperties.js | 7 + .../HardwareSelectionForm.js | 108 +++++ .../HardwareSelectionScripts.js | 25 + .../QuantMEHardwareSelectionHandler.js | 446 ++++++++++-------- .../quantme/resources/quantum4bpmn.json | 5 + components/bpmn-q/webpack.config.js | 1 + 12 files changed, 505 insertions(+), 185 deletions(-) create mode 100644 components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionForm.js diff --git a/components/bpmn-q/modeler-component/editor/util/IoUtilities.js b/components/bpmn-q/modeler-component/editor/util/IoUtilities.js index a757b8d0..49c5f8f6 100644 --- a/components/bpmn-q/modeler-component/editor/util/IoUtilities.js +++ b/components/bpmn-q/modeler-component/editor/util/IoUtilities.js @@ -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"); @@ -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( diff --git a/components/bpmn-q/modeler-component/extensions/quantme/Constants.js b/components/bpmn-q/modeler-component/extensions/quantme/Constants.js index 5890aef3..85213706 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/Constants.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/Constants.js @@ -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"; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QuantMETab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QuantMETab.js index ae475faf..136653e6 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QuantMETab.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QuantMETab.js @@ -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()); @@ -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) { @@ -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; @@ -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); @@ -178,6 +190,20 @@ export default function QuantMETab() { /> + + NISQ Analyzer UI Endpoint: + + + setNisqAnalyzerUiEndpoint(event.target.value) + } + /> + +

QProv

@@ -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(); }; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js index d78f7f08..a4a9bf37 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js @@ -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 */ diff --git a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js index 901de03f..4b21c0d7 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js @@ -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: diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js index 8fd116a5..ca64211a 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js @@ -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"; @@ -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 ( + + ); +} export function CalibrationMethodEntry({ element }) { const modeling = useService("modeling"); const translate = diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMETaskProperties.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMETaskProperties.js index f2e8309b..90248b83 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMETaskProperties.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMETaskProperties.js @@ -6,6 +6,7 @@ import { import { AlgorithmEntry, AlphaEntry, + AutomatedSelectionEntry, CalibrationMethodEntry, DNNHiddenLayersEntry, EncodingSchemaEntry, @@ -268,6 +269,12 @@ export function HardwareSelectionSubprocessProperties(element) { component: SelectionStrategyEntry, isEdited: isTextFieldEntryEdited, }, + { + id: consts.AUTOMATED_SELECTION, + element, + component: AutomatedSelectionEntry, + isEdited: isTextFieldEntryEdited, + }, ]; } diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionForm.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionForm.js new file mode 100644 index 00000000..835b8f11 --- /dev/null +++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionForm.js @@ -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 = `
+
+ + +
+ + + + + +
+ +`; + return hardwareSelectionForm; +} diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionScripts.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionScripts.js index ca49f97e..7bd8649d 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionScripts.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/HardwareSelectionScripts.js @@ -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); +} +`; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js index 5ee0bd61..8f9aeafa 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js @@ -10,6 +10,7 @@ */ import { + CONVERT_CIRCUIT, INVOKE_NISQ_ANALYZER_SCRIPT, INVOKE_TRANSFORMATION_SCRIPT, RETRIEVE_FRAGMENT_SCRIPT_PREFIX, @@ -50,6 +51,8 @@ export async function replaceHardwareSelectionSubprocess( let commandStack = modeler.get("commandStack"); let moddle = modeler.get("moddle"); + const automatedSelection = subprocess.automatedSelection; + // replace QuantumHardwareSelectionSubprocess with traditional subprocess let element = bpmnReplace.replaceElement(elementRegistry.get(subprocess.id), { type: "bpmn:SubProcess", @@ -61,6 +64,7 @@ export async function replaceHardwareSelectionSubprocess( selectionStrategy: undefined, providers: undefined, simulatorsAllowed: undefined, + automatedSelection: undefined, }); // retrieve business object of the new element @@ -99,87 +103,274 @@ export async function replaceHardwareSelectionSubprocess( // connect start event and gateway modeling.connect(startEvent, splittingGateway, { type: "bpmn:SequenceFlow" }); - // add task to invoke the NISQ Analyzer and connect it - let invokeHardwareSelection = createLayoutedShape( + if (automatedSelection) { + // add task to invoke the NISQ Analyzer and connect it + let invokeHardwareSelection = createLayoutedShape( + modeling, + { type: "bpmn:ScriptTask" }, + { x: 50, y: 50 }, + element, + {} + ); + let invokeHardwareSelectionBo = elementRegistry.get( + invokeHardwareSelection.id + ).businessObject; + invokeHardwareSelectionBo.name = "Invoke NISQ Analyzer"; + invokeHardwareSelectionBo.scriptFormat = "groovy"; + invokeHardwareSelectionBo.script = INVOKE_NISQ_ANALYZER_SCRIPT; + invokeHardwareSelectionBo.asyncBefore = true; + + // add NISQ Analyzer endpoint, providers attribute, and simulatorAllowed attribute as input parameters + let invokeHardwareSelectionInOut = getCamundaInputOutput( + invokeHardwareSelectionBo, + bpmnFactory + ); + nisqAnalyzerEndpoint += nisqAnalyzerEndpoint.endsWith("/") ? "" : "/"; + invokeHardwareSelectionInOut.inputParameters.push( + bpmnFactory.create("camunda:InputParameter", { + name: "camunda_endpoint", + value: camundaEndpoint, + }) + ); + invokeHardwareSelectionInOut.inputParameters.push( + bpmnFactory.create("camunda:InputParameter", { + name: "nisq_analyzer_endpoint_qpu_selection", + value: nisqAnalyzerEndpoint + consts.NISQ_ANALYZER_QPU_SELECTION_PATH, + }) + ); + invokeHardwareSelectionInOut.inputParameters.push( + bpmnFactory.create("camunda:InputParameter", { + name: "providers", + value: subprocess.providers, + }) + ); + invokeHardwareSelectionInOut.inputParameters.push( + bpmnFactory.create("camunda:InputParameter", { + name: "simulators_allowed", + value: subprocess.simulatorsAllowed, + }) + ); + + // connect gateway with selection path and add condition + let selectionFlow = modeling.connect( + splittingGateway, + invokeHardwareSelection, + { type: "bpmn:SequenceFlow" } + ); + let selectionFlowBo = elementRegistry.get(selectionFlow.id).businessObject; + selectionFlowBo.name = "no"; + let selectionFlowCondition = bpmnFactory.create("bpmn:FormalExpression"); + selectionFlowCondition.body = + '${execution.hasVariable("already_selected") == false || already_selected == false}'; + selectionFlowBo.conditionExpression = selectionFlowCondition; + + // add task implementing the defined selection strategy and connect it + let selectionTask = addSelectionStrategyTask( + subprocess.selectionStrategy, + element, + elementRegistry, + modeling + ); + if (selectionTask === undefined) { + return false; + } + let selectionTaskBo = elementRegistry.get(selectionTask.id).businessObject; + selectionTaskBo.asyncBefore = true; + modeling.connect(invokeHardwareSelection, selectionTask, { + type: "bpmn:SequenceFlow", + }); + insertTasks( + modeling, + elementRegistry, + bpmnFactory, + commandStack, + moddle, + element, + splittingGateway, + selectionTask, + hardwareSelectionFragment, + transformationFrameworkEndpoint, + camundaEndpoint + ); + return true; + } else { + let task = modeling.createShape( + { type: "bpmn:ScriptTask" }, + { x: 50, y: 50 }, + element, + {} + ); + let taskBo = elementRegistry.get(task.id).businessObject; + taskBo.name = "Create Circuit File"; + taskBo.scriptFormat = "groovy"; + taskBo.script = CONVERT_CIRCUIT; + let flow = modeling.connect(splittingGateway, task, { + type: "bpmn:SequenceFlow", + }); + let flowBo = elementRegistry.get(flow.id).businessObject; + flowBo.name = "no"; + let flowCondition = bpmnFactory.create("bpmn:FormalExpression"); + flowCondition.body = + '${execution.hasVariable("already_selected") == false || already_selected == false}'; + flowBo.conditionExpression = flowCondition; + let userHardwareSelection = modeling.createShape( + { type: "bpmn:UserTask" }, + { x: 50, y: 50 }, + element, + {} + ); + let userHardwareSelectionBo = elementRegistry.get( + userHardwareSelection.id + ).businessObject; + userHardwareSelectionBo.name = "Invoke NISQ Analyzer UI"; + userHardwareSelectionBo.$attrs["camunda:assignee"] = "demo"; + userHardwareSelectionBo.$attrs["camunda:formKey"] = + "embedded:deployment:hardwareSelection.html"; + modeling.connect(task, userHardwareSelection, { + type: "bpmn:SequenceFlow", + }); + insertTasks( + modeling, + elementRegistry, + bpmnFactory, + commandStack, + moddle, + element, + splittingGateway, + userHardwareSelection, + hardwareSelectionFragment, + transformationFrameworkEndpoint, + camundaEndpoint + ); + return true; + } +} + +/** + * Add and return a task implementing the given selection strategy + */ +function addSelectionStrategyTask( + selectionStrategy, + parent, + elementRegistry, + modeling +) { + console.log("Adding task for selection strategy: %s", selectionStrategy); + + if (selectionStrategy === undefined) { + return addShortestQueueSelectionStrategy(parent, elementRegistry, modeling); + } else if (!consts.SELECTION_STRATEGY_LIST.includes(selectionStrategy)) { + NotificationHandler.getInstance().displayNotification({ + type: "info", + title: "Transformation Unsuccessful!", + content: + "The chosen selection strategy is not supported. Leave blank to use default strategy: Shortest-Queue", + duration: 7000, + }); + return undefined; + } + + switch (selectionStrategy) { + case consts.SELECTION_STRATEGY_SHORTEST_QUEUE_SIZE: + return addShortestQueueSelectionStrategy( + parent, + elementRegistry, + modeling + ); + default: + console.log("Selection strategy not supported. Aborting!"); + return undefined; + } +} + +/** + * Add a task implementing the Shortest-Queue selection strategy + */ +function addShortestQueueSelectionStrategy(parent, elementRegistry, modeling) { + let task = createLayoutedShape( modeling, { type: "bpmn:ScriptTask" }, { x: 50, y: 50 }, - element, + parent, {} ); - let invokeHardwareSelectionBo = elementRegistry.get( - invokeHardwareSelection.id - ).businessObject; - invokeHardwareSelectionBo.name = "Invoke NISQ Analyzer"; - invokeHardwareSelectionBo.scriptFormat = "groovy"; - invokeHardwareSelectionBo.script = INVOKE_NISQ_ANALYZER_SCRIPT; - invokeHardwareSelectionBo.asyncBefore = true; - - // add NISQ Analyzer endpoint, providers attribute, and simulatorAllowed attribute as input parameters - let invokeHardwareSelectionInOut = getCamundaInputOutput( - invokeHardwareSelectionBo, - bpmnFactory - ); - nisqAnalyzerEndpoint += nisqAnalyzerEndpoint.endsWith("/") ? "" : "/"; - invokeHardwareSelectionInOut.inputParameters.push( - bpmnFactory.create("camunda:InputParameter", { - name: "camunda_endpoint", - value: camundaEndpoint, - }) - ); - invokeHardwareSelectionInOut.inputParameters.push( - bpmnFactory.create("camunda:InputParameter", { - name: "nisq_analyzer_endpoint_qpu_selection", - value: nisqAnalyzerEndpoint + consts.NISQ_ANALYZER_QPU_SELECTION_PATH, - }) - ); - invokeHardwareSelectionInOut.inputParameters.push( - bpmnFactory.create("camunda:InputParameter", { - name: "providers", - value: subprocess.providers, - }) - ); - invokeHardwareSelectionInOut.inputParameters.push( - bpmnFactory.create("camunda:InputParameter", { - name: "simulators_allowed", - value: subprocess.simulatorsAllowed, - }) - ); + let taskBo = elementRegistry.get(task.id).businessObject; + taskBo.name = "Selecting based on Queue Size"; + taskBo.scriptFormat = "groovy"; + taskBo.script = SELECT_ON_QUEUE_SIZE_SCRIPT; + return task; +} - // connect gateway with selection path and add condition - let selectionFlow = modeling.connect( - splittingGateway, - invokeHardwareSelection, - { type: "bpmn:SequenceFlow" } +async function getHardwareSelectionFragment(subprocess) { + console.log("Extracting workflow fragment from subprocess: ", subprocess); + + // create new modeler to extract the XML of the workflow fragment + let modeler = createTempModeler(); + let elementRegistry = modeler.get("elementRegistry"); + let bpmnReplace = modeler.get("bpmnReplace"); + let modeling = modeler.get("modeling"); + + // initialize the modeler + function initializeModeler() { + return new Promise((resolve) => { + modeler.createDiagram((err, successResponse) => { + resolve(successResponse); + }); + }); + } + + await initializeModeler(); + + // retrieve root element to add extracted workflow fragment + let definitions = modeler.getDefinitions(); + let rootElement = getRootProcess(definitions); + let rootElementBo = elementRegistry.get(rootElement.id); + + // add start and end event to the new process + let startEvent = bpmnReplace.replaceElement( + elementRegistry.get(rootElement.flowElements[0].id), + { type: "bpmn:StartEvent" } ); - let selectionFlowBo = elementRegistry.get(selectionFlow.id).businessObject; - selectionFlowBo.name = "no"; - let selectionFlowCondition = bpmnFactory.create("bpmn:FormalExpression"); - selectionFlowCondition.body = - '${execution.hasVariable("already_selected") == false || already_selected == false}'; - selectionFlowBo.conditionExpression = selectionFlowCondition; - - // add task implementing the defined selection strategy and connect it - let selectionTask = addSelectionStrategyTask( - subprocess.selectionStrategy, - element, - elementRegistry, - modeling + let endEvent = createLayoutedShape( + modeling, + { type: "bpmn:EndEvent" }, + { x: 50, y: 50 }, + rootElementBo, + {} ); - if (selectionTask === undefined) { - return false; - } - let selectionTaskBo = elementRegistry.get(selectionTask.id).businessObject; - selectionTaskBo.asyncBefore = true; - modeling.connect(invokeHardwareSelection, selectionTask, { + + // insert given subprocess and connect to start and end event + let insertedSubprocess = insertShape( + definitions, + rootElementBo, + subprocess, + {}, + false, + modeler + ).element; + modeling.connect(startEvent, insertedSubprocess, { type: "bpmn:SequenceFlow", }); + modeling.connect(insertedSubprocess, endEvent, { type: "bpmn:SequenceFlow" }); - // add task implementing the transformation of the QuantME modeling constructs within the QuantumHardwareSelectionSubprocess - console.log( - "Adding extracted workflow fragment XML: ", - hardwareSelectionFragment - ); + // export xml and remove line breaks + let xml = await getXml(modeler); + return xml.replace(/(\r\n|\n|\r)/gm, ""); +} + +function insertTasks( + modeling, + elementRegistry, + bpmnFactory, + commandStack, + moddle, + element, + splittingGateway, + task, + hardwareSelectionFragment, + transformationFrameworkEndpoint, + camundaEndpoint +) { let retrieveFragment = createLayoutedShape( modeling, { type: "bpmn:ScriptTask" }, @@ -197,7 +388,7 @@ export async function replaceHardwareSelectionSubprocess( hardwareSelectionFragment + RETRIEVE_FRAGMENT_SCRIPT_SUFFIX; retrieveFragmentBo.asyncBefore = true; - modeling.connect(selectionTask, retrieveFragment, { + modeling.connect(task, retrieveFragment, { type: "bpmn:SequenceFlow", }); @@ -316,117 +507,4 @@ export async function replaceHardwareSelectionSubprocess( modeling.connect(invokeTransformedFragment, endEvent, { type: "bpmn:SequenceFlow", }); - return true; -} - -/** - * Add and return a task implementing the given selection strategy - */ -function addSelectionStrategyTask( - selectionStrategy, - parent, - elementRegistry, - modeling -) { - console.log("Adding task for selection strategy: %s", selectionStrategy); - - if (selectionStrategy === undefined) { - return addShortestQueueSelectionStrategy(parent, elementRegistry, modeling); - } else if (!consts.SELECTION_STRATEGY_LIST.includes(selectionStrategy)) { - NotificationHandler.getInstance().displayNotification({ - type: "info", - title: "Transformation Unsuccessful!", - content: - "The chosen selection strategy is not supported. Leave blank to use default strategy: Shortest-Queue", - duration: 7000, - }); - return undefined; - } - - switch (selectionStrategy) { - case consts.SELECTION_STRATEGY_SHORTEST_QUEUE_SIZE: - return addShortestQueueSelectionStrategy( - parent, - elementRegistry, - modeling - ); - default: - console.log("Selection strategy not supported. Aborting!"); - return undefined; - } -} - -/** - * Add a task implementing the Shortest-Queue selection strategy - */ -function addShortestQueueSelectionStrategy(parent, elementRegistry, modeling) { - let task = createLayoutedShape( - modeling, - { type: "bpmn:ScriptTask" }, - { x: 50, y: 50 }, - parent, - {} - ); - let taskBo = elementRegistry.get(task.id).businessObject; - taskBo.name = "Selecting based on Queue Size"; - taskBo.scriptFormat = "groovy"; - taskBo.script = SELECT_ON_QUEUE_SIZE_SCRIPT; - return task; -} - -async function getHardwareSelectionFragment(subprocess) { - console.log("Extracting workflow fragment from subprocess: ", subprocess); - - // create new modeler to extract the XML of the workflow fragment - let modeler = createTempModeler(); - let elementRegistry = modeler.get("elementRegistry"); - let bpmnReplace = modeler.get("bpmnReplace"); - let modeling = modeler.get("modeling"); - - // initialize the modeler - function initializeModeler() { - return new Promise((resolve) => { - modeler.createDiagram((err, successResponse) => { - resolve(successResponse); - }); - }); - } - - await initializeModeler(); - - // retrieve root element to add extracted workflow fragment - let definitions = modeler.getDefinitions(); - let rootElement = getRootProcess(definitions); - let rootElementBo = elementRegistry.get(rootElement.id); - - // add start and end event to the new process - let startEvent = bpmnReplace.replaceElement( - elementRegistry.get(rootElement.flowElements[0].id), - { type: "bpmn:StartEvent" } - ); - let endEvent = createLayoutedShape( - modeling, - { type: "bpmn:EndEvent" }, - { x: 50, y: 50 }, - rootElementBo, - {} - ); - - // insert given subprocess and connect to start and end event - let insertedSubprocess = insertShape( - definitions, - rootElementBo, - subprocess, - {}, - false, - modeler - ).element; - modeling.connect(startEvent, insertedSubprocess, { - type: "bpmn:SequenceFlow", - }); - modeling.connect(insertedSubprocess, endEvent, { type: "bpmn:SequenceFlow" }); - - // export xml and remove line breaks - let xml = await getXml(modeler); - return xml.replace(/(\r\n|\n|\r)/gm, ""); } diff --git a/components/bpmn-q/modeler-component/extensions/quantme/resources/quantum4bpmn.json b/components/bpmn-q/modeler-component/extensions/quantme/resources/quantum4bpmn.json index 6d6dbc38..624e8de4 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/resources/quantum4bpmn.json +++ b/components/bpmn-q/modeler-component/extensions/quantme/resources/quantum4bpmn.json @@ -36,6 +36,11 @@ "name": "selectionStrategy", "isAttr": true, "type": "String" + }, + { + "name": "automatedSelection", + "isAttr": true, + "type": "Boolean" } ] }, diff --git a/components/bpmn-q/webpack.config.js b/components/bpmn-q/webpack.config.js index 3f213729..c6883757 100644 --- a/components/bpmn-q/webpack.config.js +++ b/components/bpmn-q/webpack.config.js @@ -19,6 +19,7 @@ let defaultConfig = { GITHUB_TOKEN: "", OPENTOSCA_ENDPOINT: "http://localhost:1337/csars", NISQ_ANALYZER_ENDPOINT: "http://localhost:8098/nisq-analyzer", + NISQ_ANALYZER_UI_ENDPOINT: "http://localhost:5009/#/algorithms", PATTERN_ATLAS_ENDPOINT: "http://localhost:1977/patternatlas/patternLanguages/af7780d5-1f97-4536-8da7-4194b093ab1d", PATTERN_ATLAS_UI_ENDPOINT: "http://localhost:1978",