diff --git a/components/bpmn-q/modeler-component/extensions/pattern/ui/pattern-selection/PatternSelectionPlugin.js b/components/bpmn-q/modeler-component/extensions/pattern/ui/pattern-selection/PatternSelectionPlugin.js
index 47ecfeca..f2f9c93f 100644
--- a/components/bpmn-q/modeler-component/extensions/pattern/ui/pattern-selection/PatternSelectionPlugin.js
+++ b/components/bpmn-q/modeler-component/extensions/pattern/ui/pattern-selection/PatternSelectionPlugin.js
@@ -11,14 +11,40 @@
import PatternOverviewModal from "./PatternOverviewModal";
import PatternModal from "./PatternModal";
+import ProgressBarModal from "./ProgressBarModal";
import React, { PureComponent } from "react";
-import { getModeler } from "../../../../editor/ModelerHandler";
+import {
+ createTempModelerFromXml,
+ getModeler,
+} from "../../../../editor/ModelerHandler";
import { fetchDataFromEndpoint } from "../../../../editor/util/HttpUtilities";
+import {
+ getExtensionElements,
+ getRootProcess,
+ pushFormField,
+} from "../../../../editor/util/ModellingUtilities";
+import { getXml, loadDiagram } from "../../../../editor/util/IoUtilities";
+import { layout } from "../../../quantme/replacement/layouter/Layouter";
+import { INITIAL_DIAGRAM_XML } from "../../../../editor/EditorConstants";
+import {
+ attachPatternsToSubprocess,
+ changeIdOfContainedElements,
+} from "../../util/PatternUtil";
+import { getBusinessObject } from "bpmn-js/lib/util/ModelUtil";
+import { getExtension } from "../../../../editor/util/camunda-utils/ExtensionElementsUtil";
+import * as quantmeConsts from "../../../quantme/Constants";
+import {
+ getPatternAtlasEndpoint,
+ getQcAtlasEndpoint,
+} from "../../framework-config/config-manager";
+import NotificationHandler from "../../../../editor/ui/notifications/NotificationHandler";
const defaultState = {
patternOverviewOpen: false,
+ showProgressBar: false,
patternOpen: false,
- responseData: null, // Store the response data from the API
+ patterns: null,
+ responseData: null,
};
export default class PatternSelectionPlugin extends PureComponent {
@@ -28,6 +54,8 @@ export default class PatternSelectionPlugin extends PureComponent {
this.modeler = getModeler();
this.handlePatternOverviewClosed =
this.handlePatternOverviewClosed.bind(this);
+ this.handlePatternSolutionClosed =
+ this.handlePatternSolutionClosed.bind(this);
this.state = defaultState;
}
@@ -36,19 +64,431 @@ export default class PatternSelectionPlugin extends PureComponent {
try {
console.log(this.modeler.config);
const response = await fetchDataFromEndpoint(
- this.modeler.config.patternAtlasEndpoint + "/patterns"
+ getPatternAtlasEndpoint() + "/patterns"
);
console.log(response);
- this.setState({ responseData: response["_embedded"]["patternModels"] });
+ this.setState({
+ responseData: response["_embedded"]["patternModels"],
+ patterns: response["_embedded"]["patternModels"],
+ });
} catch (error) {
console.error("Error fetching data from the endpoint:", error);
}
}
async handlePatternOverviewClosed(result) {
- this.setState({ patternOverviewOpen: false, patternOpen: false });
+ this.setState({
+ patternOverviewOpen: false,
+ patternOpen: false,
+ patterns: result,
+ });
console.log(result);
+
+ if (result && result.length > 0) {
+ // If the result is not empty, show the progress bar
+ this.setState({ showProgressBar: true });
+
+ try {
+ const implementationsResponse = await fetchDataFromEndpoint(
+ getQcAtlasEndpoint() + "/atlas/implementations"
+ );
+ console.log(implementationsResponse);
+ if (implementationsResponse.content !== undefined) {
+ this.setState({
+ showProgressBar: true,
+ responseData: implementationsResponse.content,
+ patternOverviewOpen: false,
+ });
+ } else {
+ this.setState({
+ showProgressBar: false,
+ });
+ NotificationHandler.getInstance().displayNotification({
+ type: "info",
+ title: "No implementations found",
+ content:
+ "Error when fetching implementations from " +
+ getQcAtlasEndpoint(),
+ duration: 4000,
+ });
+ }
+ } catch (error) {
+ console.error(
+ "Error fetching data from implementations endpoint:",
+ error
+ );
+ NotificationHandler.getInstance().displayNotification({
+ type: "info",
+ title: "No implementations found",
+ content:
+ "Error when fetching implementations from " + getQcAtlasEndpoint(),
+ duration: 4000,
+ });
+ this.setState({ showProgressBar: false });
+ }
+ }
+ }
+
+ async handlePatternSolutionClosed(result) {
+ console.log("retrieved solutions");
+ console.log(result);
+ this.setState({ showProgressBar: false });
+ let xml = INITIAL_DIAGRAM_XML;
+ let modeler = await createTempModelerFromXml(xml);
+ let definitions = modeler.getDefinitions();
+ let rootElement = getRootProcess(definitions);
+ console.log(rootElement);
+
+ let startEvent = rootElement.flowElements[0];
+ let elementToConnect = startEvent;
+ console.log(elementToConnect);
+ let modeling = modeler.get("modeling");
+ let elementRegistry = modeler.get("elementRegistry");
+ modeling.updateProperties(elementRegistry.get(elementToConnect.id), {
+ id: "Pattern_" + elementToConnect.id,
+ });
+ let elementFactory = modeler.get("elementFactory");
+ if (result) {
+ for (let i = 0; i < result.length; i++) {
+ const solution = result[i];
+
+ let solutionModeler = await createTempModelerFromXml(result[i]);
+ let solutionDefinitions = solutionModeler.getDefinitions();
+ let solutionElementRegistry = solutionModeler.get("elementRegistry");
+ let solutionModeling = solutionModeler.get("modeling");
+ const solutionRootElement = getRootProcess(solutionDefinitions);
+ console.log("DAS SOLUTIONROOTElement");
+ console.log(solutionRootElement);
+ console.log(this.state.patterns[i]);
+ let collapsedSubprocess = elementFactory.createShape({
+ type: "bpmn:SubProcess",
+ isExpanded: true,
+ });
+
+ let shape = modeling.createShape(
+ collapsedSubprocess,
+ { x: 50, y: 50 },
+ elementRegistry.get(rootElement.id)
+ );
+ modeling.updateProperties(shape, {
+ name: this.state.patterns[i].algorithmPattern.name,
+ });
+ let sourceIdToNewShapeIdMap = {};
+ if (solution !== INITIAL_DIAGRAM_XML) {
+ let solutionFlowElements = solutionRootElement.flowElements.slice();
+
+ // Filter out elements with specific $type and type values
+ const nonFilteredElements = solutionFlowElements.filter((element) => {
+ const elementType = solutionElementRegistry.get(element.id).$type;
+ const elementCustomType = solutionElementRegistry.get(
+ element.id
+ ).type;
+
+ return !(
+ elementType === "bpmn:SequenceFlow" ||
+ elementCustomType === "bpmn:SequenceFlow"
+ );
+ });
+
+ // Sort the filtered elements based on the 'x' property
+ nonFilteredElements.sort((a, b) => {
+ const elementA = solutionElementRegistry.get(a.id);
+ const elementB = solutionElementRegistry.get(b.id);
+
+ console.log(
+ `Comparing ${elementA.id} (${elementA.x}) with ${elementB.id} (${elementB.x})`
+ );
+
+ return elementA.x - elementB.x;
+ });
+
+ // Combine the sorted filtered elements with the remaining elements
+ /**
+ const sortedSolutionFlowElements = nonFilteredElements.concat(
+ solutionFlowElements.filter((element) => {
+ const elementType = solutionElementRegistry.get(element.id).$type;
+ const elementCustomType = solutionElementRegistry.get(
+ element.id
+ ).type;
+
+ return (
+ elementType === "bpmn:SequenceFlow" ||
+ elementCustomType === "bpmn:SequenceFlow"
+ );
+ })
+ );
+
+ */
+ const sortedSolutionFlowElements = nonFilteredElements;
+ const solutionFlowElementsLength = nonFilteredElements.length;
+ let offset = 0;
+ console.log(sortedSolutionFlowElements);
+ console.log(sortedSolutionFlowElements);
+
+ for (let j = 0; j < solutionFlowElementsLength; j++) {
+ let flowElement = solutionElementRegistry.get(
+ sortedSolutionFlowElements[j].id
+ );
+
+ if (
+ flowElement.$type !== "bpmn:SequenceFlow" &&
+ flowElement.type !== "bpmn:SequenceFlow"
+ ) {
+ let type = flowElement.$type;
+ if (type === undefined) {
+ type = flowElement.type;
+ }
+ let s = elementFactory.createShape({
+ type: type,
+ x: 0,
+ y: 0,
+ isExpanded: true,
+ });
+ let updateShape;
+
+ // retrieve form fields from start events and add them to the initial start event
+ if (type === "bpmn:StartEvent") {
+ updateShape = modeling.createShape(
+ s,
+ { x: 50 + offset, y: 50 },
+ elementRegistry.get(collapsedSubprocess.id)
+ );
+ modeling.updateProperties(elementRegistry.get(updateShape.id), {
+ id: collapsedSubprocess.id + "_" + updateShape.id,
+ });
+ let extensionElements = getExtensionElements(
+ getBusinessObject(startEvent),
+ modeler.get("moddle")
+ );
+ // get form data extension
+ let form = getExtension(
+ getBusinessObject(startEvent),
+ "camunda:FormData"
+ );
+ let formextended = getExtension(
+ getBusinessObject(flowElement),
+ "camunda:FormData"
+ );
+ let script = "";
+ if (formextended) {
+ if (!form) {
+ form = modeler.get("moddle").create("camunda:FormData");
+ }
+ for (let i = 0; i < formextended.fields.length; i++) {
+ let id = formextended.fields[i].id;
+ let updatedId = id + updateShape.id;
+ formextended.fields[i].id = updatedId;
+ script += `def ${updatedId}Value = execution.getVariable("${updatedId}");\n execution.setVariable("${id}", ${updatedId}Value)\n`;
+ pushFormField(form, formextended.fields[i]);
+ }
+ extensionElements.values = [form];
+ }
+
+ modeling.updateProperties(elementRegistry.get(startEvent.id), {
+ extensionElements: extensionElements,
+ });
+
+ // if mapping is required then the script task has to inserted and the outgoing flows have to be changed
+ if (script) {
+ let mapFormFieldScriptTask = elementFactory.createShape({
+ type: "bpmn:ScriptTask",
+ });
+
+ let shape = modeling.createShape(
+ mapFormFieldScriptTask,
+ { x: 50, y: 50 },
+ elementRegistry.get(collapsedSubprocess.id)
+ );
+ let shapeBo = elementRegistry.get(shape.id).businessObject;
+
+ shapeBo.name = "Map Form Fields to Execution Variables";
+ shapeBo.scriptFormat = "groovy";
+ shapeBo.script = script;
+ shapeBo.asyncBefore = true;
+
+ let outgoingFlows = [];
+ let start = elementRegistry.get(updateShape.id);
+ flowElement.outgoing.forEach((element) => {
+ outgoingFlows.push(solutionElementRegistry.get(element.id));
+ modeling.connect(
+ shape,
+ solutionElementRegistry.get(element.target.id),
+ {
+ type: "bpmn:SequenceFlow",
+ }
+ );
+ });
+ modeling.connect(start, shape, { type: "bpmn:SequenceFlow" });
+ solutionModeling.removeElements(outgoingFlows);
+ }
+ } else if (
+ type !== quantmeConsts.CIRCUIT_CUTTING_SUBPROCESS &&
+ type !== quantmeConsts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS &&
+ type !== "bpmn:SubProcess"
+ ) {
+ /**
+ updateShape = modeling.createShape(
+ s,
+ { x: 50 + offset, y: 50 },
+ elementRegistry.get(collapsedSubprocess.id)
+ );
+ modeling.updateProperties(elementRegistry.get(updateShape.id), {
+ id: collapsedSubprocess.id + "_" + updateShape.id,
+ });
+ */
+ updateShape = modeling.createShape(
+ flowElement,
+ { x: 442 + offset, y: 100 },
+ elementRegistry.get(collapsedSubprocess.id)
+ );
+ modeling.updateProperties(elementRegistry.get(updateShape.id), {
+ id: collapsedSubprocess.id + "_" + updateShape.id,
+ });
+ updateShape.di.id =
+ collapsedSubprocess.id + "_" + updateShape.id + "_di";
+ } else {
+ console.log("Flowelement");
+ console.log(flowElement);
+ /**
+ let flows = [];
+ for(let i = 0; i < flowElement.incoming.length; i++){
+ flows.push(solutionElementRegistry.get(flowElement.incoming[i].id));
+ }
+ for(let i = 0; i < flowElement.outgoing.length; i++){
+ flows.push(solutionElementRegistry.get(flowElement.outgoing[i].id));
+ }
+
+ solutionModeling.removeElements(flows);
+ */
+ console.log(
+ solutionElementRegistry.get(sortedSolutionFlowElements[j].id)
+ );
+ console.log(flowElement);
+
+ updateShape = modeling.createShape(
+ flowElement,
+ { x: 442 + offset, y: 100 },
+ elementRegistry.get(collapsedSubprocess.id)
+ );
+ updateShape.di.id =
+ collapsedSubprocess.id + "_" + updateShape.id + "_di";
+ console.log(updateShape);
+
+ // change id of solution elements since each id must be unique
+ changeIdOfContainedElements(
+ flowElement,
+ collapsedSubprocess,
+ solutionModeling,
+ solutionElementRegistry,
+ collapsedSubprocess.id + "_" + updateShape.id
+ );
+ modeling.updateProperties(elementRegistry.get(updateShape.id), {
+ id: collapsedSubprocess.id + "_" + updateShape.id,
+ });
+ console.log(updateShape);
+ }
+ offset += 150;
+
+ sourceIdToNewShapeIdMap[sortedSolutionFlowElements[j].id] =
+ updateShape.id;
+ }
+ }
+
+ solutionFlowElements = solutionRootElement.flowElements.slice();
+
+ // Filter out elements with specific $type and type values
+ const sequenceFlows = solutionFlowElements.filter((element) => {
+ const elementType = solutionElementRegistry.get(element.id).$type;
+ const elementCustomType = solutionElementRegistry.get(
+ element.id
+ ).type;
+
+ return (
+ elementType === "bpmn:SequenceFlow" ||
+ elementCustomType === "bpmn:SequenceFlow"
+ );
+ });
+ console.log(sequenceFlows);
+
+ for (let j = 0; j < solutionRootElement.flowElements.length; j++) {
+ let flowElement = solutionElementRegistry.get(
+ solutionRootElement.flowElements[j].id
+ );
+ if (flowElement.type === "bpmn:SequenceFlow") {
+ // Retrieve the id of the newly created shape using the map
+ let sourceId = sourceIdToNewShapeIdMap[flowElement.source.id];
+ let newTargetId = sourceIdToNewShapeIdMap[flowElement.target.id];
+ console.log(
+ "connect source " + sourceId + "and target" + newTargetId
+ );
+ modeling.connect(
+ elementRegistry.get(sourceId),
+ elementRegistry.get(newTargetId),
+ { type: "bpmn:SequenceFlow" }
+ );
+ }
+ }
+ } else {
+ let collapsedSubprocessStartEvent = elementFactory.createShape({
+ type: "bpmn:StartEvent",
+ });
+ modeling.createShape(
+ collapsedSubprocessStartEvent,
+ { x: 100 + 200, y: 100 },
+ elementRegistry.get(collapsedSubprocess.id)
+ );
+ }
+ elementFactory.createConnection({
+ type: "bpmn:SequenceFlow",
+ source: elementToConnect,
+ target: collapsedSubprocess,
+ });
+ modeling.connect(
+ elementRegistry.get(elementToConnect.id),
+ elementRegistry.get(collapsedSubprocess.id),
+ { type: "bpmn:SequenceFlow" }
+ );
+ elementToConnect = collapsedSubprocess;
+
+ console.log("attach patterns to each subprocess");
+ attachPatternsToSubprocess(
+ elementToConnect,
+ this.state.patterns[i],
+ modeling
+ );
+ }
+ let endEvent = elementFactory.createShape({
+ type: "bpmn:EndEvent",
+ });
+ modeling.createShape(
+ endEvent,
+ { x: 50, y: 50 },
+ elementRegistry.get(rootElement.id)
+ );
+ modeling.connect(
+ elementRegistry.get(elementToConnect.id),
+ elementRegistry.get(endEvent.id),
+ { type: "bpmn:SequenceFlow" }
+ );
+
+ let collapsedXml = await getXml(modeler);
+ loadDiagram(collapsedXml, getModeler());
+ modeler = await createTempModelerFromXml(collapsedXml);
+ elementRegistry = modeler.get("elementRegistry");
+ modeling = modeler.get("modeling");
+ definitions = modeler.getDefinitions();
+ rootElement = getRootProcess(definitions);
+
+ console.log(rootElement);
+ let elements = [];
+ for (let i = 0; i < rootElement.flowElements; i++) {
+ elements.push(elementRegistry.get(rootElement.flowElement[i].id));
+ }
+ layout(modeling, elementRegistry, rootElement);
+ collapsedXml = await getXml(modeler);
+ loadDiagram(collapsedXml, getModeler());
+ }
}
render() {
@@ -74,6 +514,7 @@ export default class PatternSelectionPlugin extends PureComponent {
onClose={() =>
this.setState({ patternOverviewOpen: true, patternOpen: false })
}
+ onCancel={() => this.setState({ patternOpen: false })}
/>
)}
{this.state.patternOverviewOpen && (
@@ -82,6 +523,13 @@ export default class PatternSelectionPlugin extends PureComponent {
responseData={this.state.responseData}
/>
)}
+ {this.state.showProgressBar && (
+
+ )}
>
);
}
diff --git a/components/bpmn-q/modeler-component/extensions/pattern/ui/pattern-selection/ProgressBarModal.js b/components/bpmn-q/modeler-component/extensions/pattern/ui/pattern-selection/ProgressBarModal.js
new file mode 100644
index 00000000..4102fa00
--- /dev/null
+++ b/components/bpmn-q/modeler-component/extensions/pattern/ui/pattern-selection/ProgressBarModal.js
@@ -0,0 +1,130 @@
+import React, { useState, useEffect } from "react";
+import Modal from "../../../../editor/ui/modal/Modal";
+import {
+ fetchDataFromEndpoint,
+ fetchSolutionFromEndpoint,
+} from "../../../../editor/util/HttpUtilities";
+import { INITIAL_DIAGRAM_XML } from "../../../../editor/EditorConstants";
+import { getQcAtlasEndpoint } from "../../framework-config/config-manager";
+
+const Title = Modal.Title || (({ children }) =>
{children}
);
+const Body = Modal.Body || (({ children }) =>
{children}
);
+const Footer = Modal.Footer || (({ children }) =>
{children}
);
+
+const ProgressBarModal = ({ responseData, selectedPatterns, onClose }) => {
+ const [progress, setProgress] = useState(0);
+ const [currentIdIndex, setCurrentIdIndex] = useState(0);
+ const [solutions, setSolutions] = useState([]);
+ const qcAtlasEndpoint = getQcAtlasEndpoint();
+
+ useEffect(() => {
+ const fetchData = async () => {
+ console.log("extract now");
+ console.log(selectedPatterns);
+ const totalRequests = responseData.length;
+ const fetchedSolutions = [];
+ let foundSolution = false;
+ for (let j = 0; j < selectedPatterns.length; j++) {
+ // get the selected algorithm id
+ let algorithmPatternId = selectedPatterns[j].algorithmPattern.id;
+
+ for (let i = 0; i < totalRequests; i++) {
+ const { id, implementedAlgorithmId, patterns } = responseData[i];
+ if (patterns !== undefined && patterns.length > 0) {
+ const linkedPattern = patterns[0].split("/");
+
+ const linkedAlgorithmPatternId =
+ linkedPattern[linkedPattern.length - 1];
+ if (algorithmPatternId === linkedAlgorithmPatternId) {
+ try {
+ if (implementedAlgorithmId !== undefined) {
+ console.log(
+ `Retrieving solution for algorithm ${implementedAlgorithmId} and implementation ${id}`
+ );
+ setCurrentIdIndex(i);
+ console.log(
+ `${qcAtlasEndpoint}/atlas/algorithms/${implementedAlgorithmId}/implementations/${id}/implementation-packages`
+ );
+ const response = await fetchDataFromEndpoint(
+ `${qcAtlasEndpoint}/atlas/algorithms/${implementedAlgorithmId}/implementations/${id}/implementation-packages`
+ );
+
+ if (response && response.content.length > 0) {
+ // currently takes the first solution
+ let solutionId = response.content[0].id;
+ const solutionPackage = await fetchSolutionFromEndpoint(
+ `${qcAtlasEndpoint}/atlas/algorithms/${implementedAlgorithmId}/implementations/${id}/implementation-packages/${solutionId}/file/content`
+ );
+ console.log(solutionPackage);
+
+ fetchedSolutions.push(solutionPackage);
+ foundSolution = true;
+ if (solutionPackage.ok) {
+ fetchedSolutions.push(solutionPackage.content);
+ }
+ }
+ }
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ onClose();
+ }
+ }
+ }
+ }
+ if (!foundSolution) {
+ // if no solution exists then an empty subprocess needs to be created
+ fetchedSolutions.push(INITIAL_DIAGRAM_XML);
+ }
+ foundSolution = false;
+ }
+ setSolutions(fetchedSolutions);
+ setProgress(100);
+ };
+
+ fetchData();
+ }, [responseData]);
+
+ // Render the component with the updated progress value
+ return (
+
+ Retrieve solutions
+
+
+
+
+ Retrieving solution for algorithm{" "}
+ {responseData?.length > 0 &&
+ responseData[currentIdIndex]?.implementedAlgorithmId}{" "}
+ and implementation{" "}
+ {responseData?.length > 0 && responseData[currentIdIndex]?.id}
+
+
+
+
+
+
+
+ );
+};
+
+export default ProgressBarModal;
diff --git a/components/bpmn-q/modeler-component/extensions/pattern/util/PatternUtil.js b/components/bpmn-q/modeler-component/extensions/pattern/util/PatternUtil.js
new file mode 100644
index 00000000..c5e1a55a
--- /dev/null
+++ b/components/bpmn-q/modeler-component/extensions/pattern/util/PatternUtil.js
@@ -0,0 +1,253 @@
+/**
+ * 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
+ */
+import * as consts from "../Constants";
+import * as quantmeConsts from "../../quantme/Constants";
+import { computeDimensionsOfElement } from "../../quantme/replacement/layouter/Layouter";
+export function attachPatternsToSubprocess(subprocess, patterns, modeling) {
+ let dimensions = computeDimensionsOfElement(subprocess);
+ console.log(subprocess);
+ const patternPrefix = "pattern:";
+ const patternSpacing = 65;
+ let indexPatternOutsideSubprocess = 0;
+ const createPatterns = (patternList, offsetX) => {
+ for (let i = 0; i < patternList.length; i++) {
+ const patternName = patternList[i].name.replace(/[\s-]/g, "");
+ console.log("add pattern", patternName);
+
+ let patternX = subprocess.x + patternSpacing * (i + offsetX);
+ let patternY = subprocess.y + dimensions.height;
+ indexPatternOutsideSubprocess = i;
+ // start from the top if x coordinate exceeds subprocess size
+ if (patternX > subprocess.x + dimensions.width) {
+ patternY = subprocess.y;
+ patternX =
+ subprocess.x +
+ patternSpacing * (i - indexPatternOutsideSubprocess - 1 + offsetX);
+ }
+
+ console.log(patternX);
+ console.log(patternY);
+ createPattern(
+ modeling,
+ patternPrefix,
+ patternName,
+ patternX,
+ patternY,
+ subprocess
+ );
+ }
+ };
+
+ createPatterns(patterns.behavioralPattern, 0);
+ createPatterns(
+ patterns.augmentationPattern,
+ patterns.behavioralPattern.length
+ );
+}
+
+function createPattern(modeling, patternPrefix, patternName, x, y, subprocess) {
+ const pattern = modeling.createShape(
+ { type: patternPrefix + patternName },
+ { x: x, y: y },
+ subprocess,
+ { attach: true }
+ );
+
+ modeling.updateProperties(pattern, {
+ attachedToRef: subprocess.businessObject,
+ });
+}
+export function attachPatternsToSuitableConstruct(
+ construct,
+ patternType,
+ modeling
+) {
+ console.log("attach pattern to suitable modeling construct");
+ console.log(construct);
+ if (construct !== undefined) {
+ let type = construct.$type;
+ if (type === undefined) {
+ type = construct.type;
+ }
+ let containsPattern = false;
+ let containsForbiddenPatternCombinations = false;
+ if (construct.attachers !== undefined) {
+ for (let i = 0; i < construct.attachers.length; i++) {
+ let eventType = construct.attachers[i].type;
+ console.log(patternType);
+ console.log(eventType);
+ if (patternType === eventType) {
+ containsPattern = true;
+ }
+ }
+ containsForbiddenPatternCombinations = checkForbiddenPatternCombinations(
+ construct,
+ patternType
+ );
+ console.log(containsForbiddenPatternCombinations);
+
+ if (!containsPattern && !containsForbiddenPatternCombinations) {
+ console.log(patternType);
+ console.log(consts.WARM_STARTING_PATTERNS.includes(patternType));
+ if (
+ patternType === consts.BIASED_INITIAL_STATE &&
+ type === quantmeConsts.QUANTUM_CIRCUIT_LOADING_TASK
+ ) {
+ attachPatternToShape(construct, patternType, modeling);
+ console.log("added biased initial state");
+ }
+ if (
+ patternType === consts.VARIATIONAL_PARAMETER_TRANSFER &&
+ type === quantmeConsts.QUANTUM_CIRCUIT_EXECUTION_TASK
+ ) {
+ attachPatternToShape(construct, patternType, modeling);
+ console.log("added variational parameter transfer");
+ }
+ if (
+ patternType === consts.ERROR_CORRECTION &&
+ (type === quantmeConsts.QUANTUM_CIRCUIT_EXECUTION_TASK ||
+ type === quantmeConsts.QUANTUM_CIRCUIT_LOADING_TASK)
+ ) {
+ attachPatternToShape(construct, patternType, modeling);
+ console.log("added error correction", construct.id);
+ }
+ if (
+ (patternType === consts.GATE_ERROR_MITIGATION ||
+ patternType === consts.READOUT_ERROR_MITIGATION) &&
+ type === quantmeConsts.QUANTUM_CIRCUIT_EXECUTION_TASK
+ ) {
+ attachPatternToShape(construct, patternType, modeling);
+ console.log("added mitigation", construct.id);
+ }
+
+ if (
+ patternType === consts.CIRCUIT_CUTTING &&
+ type === quantmeConsts.QUANTUM_CIRCUIT_EXECUTION_TASK
+ ) {
+ attachPatternToShape(construct, patternType, modeling);
+ console.log("added cutting");
+ }
+
+ if (
+ consts.BEHAVIORAL_PATTERNS.includes(patternType) &&
+ type === "bpmn:SubProcess"
+ ) {
+ attachPatternToShape(construct, patternType, modeling);
+ console.log("attached behavioral pattern");
+ console.log(patternType);
+ console.log(construct);
+ console.log("added behavioral pattern");
+ }
+ }
+ }
+ return !containsPattern && !containsForbiddenPatternCombinations;
+ }
+}
+
+function attachPatternToShape(shape, patternType, modeling) {
+ let pattern = modeling.createShape(
+ { type: patternType },
+ { x: shape.x + shape.width, y: shape.y + shape.height },
+ shape,
+ { attach: true }
+ );
+ modeling.updateProperties(pattern, {
+ attachedToRef: shape.businessObject,
+ });
+ console.log(pattern);
+}
+
+export function changeIdOfContainedElements(
+ subprocess,
+ parent,
+ modeling,
+ elementRegistry,
+ id
+) {
+ console.log(
+ "change id of contained elements of subprocess",
+ subprocess.id,
+ parent.id,
+ id
+ );
+ console.log(subprocess);
+ for (let i = 0; i < subprocess.children.length; i++) {
+ let child = subprocess.children[i];
+
+ console.log(child);
+ console.log(elementRegistry.get(child.id));
+
+ modeling.updateProperties(elementRegistry.get(child.id), {
+ id: id + "_" + child.id,
+ });
+ child.di.id = id + "_" + child.id + "_di";
+
+ if (
+ child.$type === "bpmn:SubProcess" ||
+ child.$type === quantmeConsts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS ||
+ child.$type === quantmeConsts.CIRCUIT_CUTTING_SUBPROCESS
+ ) {
+ changeIdOfContainedElements(
+ child,
+ child.parent,
+ modeling,
+ elementRegistry,
+ id + "_" + child.id
+ );
+ }
+ }
+}
+
+/**
+ * Checks whether the attached patterns conflict with the pattern intended to be attached to the construct.
+ * @param construct The construct to which the pattern is intended to be attached.
+ * @param patternType The type of the pattern being considered.
+ * @returns True if there is a conflict, false otherwise.
+ */
+export function checkForbiddenPatternCombinations(construct, patternType) {
+ console.log("attach patternType to construct", patternType, construct.id);
+ console.log(construct);
+ console.log(construct.attachers);
+ if (construct.attachers !== undefined) {
+ if (patternType === consts.ERROR_CORRECTION) {
+ const forbiddenPatterns = construct.attachers.filter(
+ (pattern) =>
+ pattern.type === consts.GATE_ERROR_MITIGATION ||
+ pattern.type === consts.READOUT_ERROR_MITIGATION
+ );
+ return forbiddenPatterns.length > 0;
+ }
+ if (
+ patternType === consts.GATE_ERROR_MITIGATION ||
+ patternType === consts.READOUT_ERROR_MITIGATION
+ ) {
+ const forbiddenPatterns = construct.attachers.filter(
+ (pattern) => pattern.type === consts.ERROR_CORRECTION
+ );
+ return forbiddenPatterns.length > 0;
+ }
+ if (patternType === consts.ORCHESTRATED_EXECUTION) {
+ const forbiddenPatterns = construct.attachers.filter(
+ (pattern) => pattern.type === consts.PRE_DEPLOYED_EXECUTION
+ );
+ return forbiddenPatterns.length > 0;
+ }
+ if (patternType === consts.PRE_DEPLOYED_EXECUTION) {
+ const forbiddenPatterns = construct.attachers.filter(
+ (pattern) => pattern.type === consts.ORCHESTRATED_EXECUTION
+ );
+ console.log(forbiddenPatterns.length > 0);
+ console.log("predeployed attached or not");
+ return forbiddenPatterns.length > 0;
+ }
+ }
+ return false;
+}
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/Constants.js b/components/bpmn-q/modeler-component/extensions/quantme/Constants.js
index 85213706..25f621a5 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/Constants.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/Constants.js
@@ -79,6 +79,7 @@ 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 WARM_STARTING_PATTERN = "warmStartingPattern";
export const EXECUTION_RESULT = "executionResult";
export const EVALUATION_RESULT = "evaluationResult";
@@ -133,6 +134,7 @@ export const QUANTME_ATTRIBUTES = [
MAX_SUBCIRCUIT_WIDTH,
MAX_NUMBER_OF_CUTS,
MAXIMUM_NUM_SUBCIRCUITS,
+ WARM_STARTING_PATTERN,
];
export const QUANTME_TASKS = [
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERules.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERules.js
index 10e5b92e..d8e5e227 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERules.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERules.js
@@ -22,7 +22,6 @@ export default class QuantMERules extends RuleProvider {
this.addRule("shape.create", 11000, function (context) {
var shape = context.shape,
target = context.target;
-
if (
shape.type.includes("Policy") &&
!consts.QUANTME_TASKS.includes(target.type)
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 fd9455bd..3f87241d 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
@@ -944,8 +944,7 @@ export function MaxSubCircuitWidthEntry({ element }) {
};
const hidden = function () {
- let cuttingMethod = element.businessObject.cuttingMethod;
- return !(cuttingMethod === "qiskit");
+ return false;
};
return (
@@ -980,8 +979,7 @@ export function MaxNumberOfCutsEntry({ element }) {
};
const hidden = function () {
- let cuttingMethod = element.businessObject.cuttingMethod;
- return !(cuttingMethod === "qiskit");
+ return false;
};
return (
@@ -1016,8 +1014,7 @@ export function MaxNumberSubcircuitsEntry({ element }) {
};
const hidden = function () {
- let cuttingMethod = element.businessObject.cuttingMethod;
- return !(cuttingMethod === "qiskit");
+ return false;
};
return (
@@ -1652,3 +1649,57 @@ export function ErrorCorrectionMethodEntry({ element }) {
/>
);
}
+
+export function WarmStartingPatternEntry({ element }) {
+ const modeling = useService("modeling");
+ const translate =
+ useService("translate") ||
+ function (str) {
+ return str;
+ };
+ const debounce = useService("debounceInput");
+
+ const getValue = function () {
+ return element.businessObject.warmStartingPattern;
+ };
+
+ const setValue = function (newValue) {
+ return modeling.updateProperties(element, {
+ warmStartingPattern: newValue,
+ });
+ };
+
+ const selectOptions = [
+ {
+ value: "biasedInitialState",
+ label: "Biased Initial State",
+ },
+ {
+ value: "chainedOptimization",
+ label: "Chained Optimization",
+ },
+ {
+ value: "preTrainedFeatureExtractor",
+ label: "Pre-Trained Feature Extractor",
+ },
+ {
+ value: "variationalParameterTransfer",
+ label: "Variational Parameter Transfer",
+ },
+ ];
+
+ const getOptions = function () {
+ return selectOptions;
+ };
+
+ return (
+
+ );
+}
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 90248b83..f40e4ac9 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
@@ -50,6 +50,7 @@ import {
InitialStateEntry,
EvaluationResultEntry,
ErrorCorrectionMethodEntry,
+ WarmStartingPatternEntry,
} from "./QuantMEPropertyEntries";
/**
@@ -424,6 +425,12 @@ export function VariationalQuantumAlgorithmTaskEntries(element) {
export function WarmStartingTaskEntries(element) {
// add providers, simulatorsAllowed, and selectionStrategy attributes
return [
+ {
+ id: consts.WARM_STARTING_PATTERN,
+ element,
+ component: WarmStartingPatternEntry,
+ isEdited: isTextFieldEntryEdited,
+ },
{
id: consts.WARM_STARTING_METHOD,
element,
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMEMatcher.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMEMatcher.js
index 5fedac8b..2c9bb412 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMEMatcher.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMEMatcher.js
@@ -18,14 +18,12 @@ import * as consts from "../Constants";
import { isQuantMETask } from "../utilities/Utilities";
import {
ALGORITHM,
- ALPHA,
CALIBRATION_METHOD,
CLASSICAL_ALGORTHM,
COST_FUNCTION,
CUTTING_METHOD,
DNN_HIDDEN_LAYER,
ENCODING_SCHEMA,
- ETA,
LEARNING_RATE,
MAX_AGE,
MAX_CM_SIZE,
@@ -382,9 +380,7 @@ function matchResultEvaluationTask(detectorElement, task) {
task.costFunction,
true,
COST_FUNCTION
- ) &&
- matchesProperty(detectorElement.alpha, task.alpha, false, ALPHA) &&
- matchesProperty(detectorElement.eta, task.eta, false, ETA)
+ )
);
}
@@ -416,41 +412,22 @@ function matchCircuitCuttingSubprocess(detectorElement, task) {
}
/**
- * Compare the properties of QuantumCircuitExecutionTasks
+ * Compare the properties of Circuit Cutting Task
*/
function matchCircuitCuttingTask(detectorElement, task) {
- return (
- matchesProperty(
- detectorElement.cuttingMethod,
- task.cuttingMethod,
- true,
- CUTTING_METHOD
- ) &&
- matchesProperty(
- detectorElement.maxSubCircuitWidth,
- task.maxSubCircuitWidth,
- false,
- MAX_SUBCIRCUIT_WIDTH
- ) &&
- matchesProperty(
- detectorElement.maxNumberOfCuts,
- task.maxNumberOfCuts,
- false,
- MAX_NUMBER_OF_CUTS
- ) &&
- matchesProperty(
- detectorElement.maxNumSubCircuits,
- task.maxNumSubCircuits,
- false,
- MAXIMUM_NUM_SUBCIRCUITS
- )
+ return matchesProperty(
+ detectorElement.cuttingMethod,
+ task.cuttingMethod,
+ true,
+ CUTTING_METHOD
);
}
/**
- * Compare the properties of QuantumCircuitExecutionTasks
+ * Compare the properties of Result Combination Tak
*/
function matchCuttingResultCombinationTask(detectorElement, task) {
+ console.log(detectorElement.cuttingMethod);
return matchesProperty(
detectorElement.cuttingMethod,
task.cuttingMethod,
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js
index 9108d8b3..7283c2bb 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js
@@ -12,6 +12,7 @@
import { getDi, is } from "bpmn-js/lib/util/ModelUtil";
import { isFlowLikeElement } from "../../../../editor/util/ModellingUtilities";
import { ON_DEMAND_POLICY, POLICIES } from "../../../opentosca/Constants";
+import { PATTERNS } from "../../../pattern/Constants";
// space between multiple boundary events of a task/subprocess
let BOUNDARY_EVENT_MARGIN = "8";
@@ -74,7 +75,8 @@ function layoutProcess(modeling, elementRegistry, process) {
// boundary events are skipped here, as they are always attached to some task and only this task has to be layouted
if (
flowElements[i].$type === "bpmn:BoundaryEvent" ||
- POLICIES.includes(flowElements[i].$type)
+ POLICIES.includes(flowElements[i].$type) ||
+ PATTERNS.includes(flowElements[i].$type)
) {
continue;
}
@@ -441,6 +443,16 @@ function layoutWithDagre(
console.log("Adding %i tasks to the graph for layouting: ", tasks.length);
for (let i = 0; i < tasks.length; i++) {
let task = tasks[i];
+ console.log(task.type);
+ if (
+ task.type === "bpmn:SubProcess" ||
+ task.type === "quantme:CircuitCuttingSubProcess" ||
+ task.type === "quantme:HardwareSelectionSubProcess"
+ ) {
+ const dimensions = computeDimensionsOfElement(task);
+ task.height = dimensions.height;
+ task.width = dimensions.width;
+ }
g.setNode(task.id, {
label: task.id,
width: task.width,
@@ -492,3 +504,38 @@ function layoutWithDagre(
}
});
}
+
+export function computeDimensionsOfElement(element) {
+ let minX = Number.MAX_SAFE_INTEGER;
+ let maxX = Number.MIN_SAFE_INTEGER;
+ let minY = Number.MAX_SAFE_INTEGER;
+ let maxY = Number.MIN_SAFE_INTEGER;
+
+ console.log(element);
+
+ element.children.forEach((child) => {
+ console.log("Get children x of child ", child.id);
+ if (child.di && child.di.bounds) {
+ const childX = child.di.bounds.x;
+ const childY = child.di.bounds.y;
+ console.log(minX);
+ minX = Math.min(minX, childX);
+ minY = Math.min(minY, childY);
+ console.log(childX);
+ console.log(maxX);
+ maxX = Math.max(maxX, childX + child.di.bounds.width);
+ maxY = Math.max(maxY, childY + child.di.bounds.height);
+ console.log(childX + child.di.bounds.width);
+ console.log(childY + child.di.bounds.height);
+ console.log(maxY);
+ }
+ });
+ console.log(maxX);
+ console.log(minX);
+ // the elements start not with the subprocess.x so we need to adjust the width
+ // add a little padding
+ const subprocessWidth = maxX - element.x - minX + 30;
+ const subprocessHeight = maxY - element.y + 10;
+
+ return { width: subprocessWidth, height: subprocessHeight };
+}
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 624e8de4..0e945c8d 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/resources/quantum4bpmn.json
+++ b/components/bpmn-q/modeler-component/extensions/quantme/resources/quantum4bpmn.json
@@ -271,37 +271,6 @@
}
]
},
- {
- "name": "WarmStartingTask",
- "superClass": ["bpmn:Task"],
- "properties": [
- {
- "name": "warmStartingMethod",
- "isAttr": true,
- "type": "String"
- },
- {
- "name": "quantumAlgorithm",
- "isAttr": true,
- "type": "String"
- },
- {
- "name": "classicalAlgorithm",
- "isAttr": true,
- "type": "String"
- },
- {
- "name": "repetitions",
- "isAttr": true,
- "type": "String"
- },
- {
- "name": "rounded",
- "isAttr": true,
- "type": "String"
- }
- ]
- },
{
"name": "ReadoutErrorMitigationTask",
"superClass": ["bpmn:Task"],
@@ -498,35 +467,14 @@
]
},
{
- "name": "ResultEvaluationTask",
+ "name": "WarmStartingTask",
"superClass": ["bpmn:Task"],
"properties": [
{
- "name": "objectiveFunction",
- "isAttr": true,
- "type": "String"
- },
- {
- "name": "costFunction",
+ "name": "warmStartingPattern",
"isAttr": true,
"type": "String"
},
- {
- "name": "eta",
- "isAttr": true,
- "type": "String"
- },
- {
- "name": "alpha",
- "isAttr": true,
- "type": "String"
- }
- ]
- },
- {
- "name": "WarmStartingTask",
- "superClass": ["bpmn:Task"],
- "properties": [
{
"name": "warmStartingMethod",
"isAttr": true,
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/CandidateDetector.js b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/CandidateDetector.js
index aff7dfbe..80357092 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/CandidateDetector.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/CandidateDetector.js
@@ -12,7 +12,11 @@
import lodash from "lodash";
import generateImage from "../../../../editor/util/camunda-utils/generateImage";
import { getRootProcess } from "../../../../editor/util/ModellingUtilities";
-import { createTempModelerFromXml } from "../../../../editor/ModelerHandler";
+import {
+ createTempModelerFromXml,
+ getModeler,
+} from "../../../../editor/ModelerHandler";
+import { getXml } from "../../../../editor/util/IoUtilities";
/**
* Find candidates within the current workflow model that can be executed efficiently using a hybrid runtime
@@ -30,7 +34,8 @@ export async function findOptimizationCandidates(modeler) {
);
// export xml of the current workflow model to enable a later image creation
- let workflowXml = await modeler.get("bpmnjs").saveXML();
+ let workflowXml = await getXml(modeler);
+ console.log(workflowXml);
// get all potential entry points for a hybrid loop
let entryPoints = findEntryPoints(rootElement);
@@ -61,7 +66,8 @@ export async function findOptimizationCandidates(modeler) {
// generate visual representation of the candidate using base64
optimizationCandidate = await visualizeCandidate(
optimizationCandidate,
- workflowXml.xml
+ workflowXml,
+ modeler
);
console.log(
@@ -88,14 +94,16 @@ export async function findOptimizationCandidates(modeler) {
* @param workflowXml the XML of the workflow the candidate belongs to
* @return the string containing the base64 encoded image
*/
-async function visualizeCandidate(optimizationCandidate, workflowXml) {
+async function visualizeCandidate(optimizationCandidate, workflowXml, modeler) {
console.log("Visualizing optimization candidate: ", optimizationCandidate);
-
+ console.log(workflowXml);
// create new modeler for the visualization
- let modeler = await createTempModelerFromXml(workflowXml);
- let modeling = modeler.get("modeling");
+ let tempModeler = await createTempModelerFromXml(workflowXml);
+ let modeling = tempModeler.get("modeling");
+ let tempElementRegistry = tempModeler.get("elementRegistry");
+ console.log(elementRegistry);
+ let rootElement = getRootProcess(tempModeler.getDefinitions());
let elementRegistry = modeler.get("elementRegistry");
- let rootElement = getRootProcess(modeler.getDefinitions());
// remove all flows that are not part of the candidate
const flowElements = lodash.cloneDeep(rootElement.flowElements);
@@ -109,7 +117,7 @@ async function visualizeCandidate(optimizationCandidate, workflowXml) {
flowElement.$type === "bpmn:SequenceFlow"
) {
// remove connection from the modeler
- let element = elementRegistry.get(flowElement.id);
+ let element = tempElementRegistry.get(flowElement.id);
modeling.removeConnection(element);
}
}
@@ -121,15 +129,20 @@ async function visualizeCandidate(optimizationCandidate, workflowXml) {
// remove all shapes that are not part of the candidate
for (let i = 0; i < flowElements.length; i++) {
let flowElement = flowElements[i];
+ console.log(flowElement);
if (
!optimizationCandidate.containedElements.some(
(e) => e.id === flowElement.id
) &&
- flowElement.$type !== "bpmn:SequenceFlow"
+ flowElement.$type !== "bpmn:SequenceFlow" &&
+ flowElement.$type !== "pattern:PredeployedExecution"
) {
// remove shape from the modeler
- let element = elementRegistry.get(flowElement.id);
- modeling.removeShape(element);
+ let element = tempElementRegistry.get(flowElement.id);
+ console.log(element);
+ if (element !== undefined) {
+ modeling.removeShape(element);
+ }
}
}
console.log(
@@ -154,7 +167,7 @@ async function visualizeCandidate(optimizationCandidate, workflowXml) {
// generate png from svg
optimizationCandidate.candidateImage = generateImage("png", svg);
- optimizationCandidate.modeler = modeler;
+ optimizationCandidate.modeler = getModeler();
return optimizationCandidate;
}
@@ -174,6 +187,16 @@ function calculateViewBox(optimizationCandidate, svg, elementRegistry) {
optimizationCandidate.containedElements[i].id
);
+ console.log(element);
+ console.log(elementRegistry);
+ console.log(optimizationCandidate);
+ console.log(optimizationCandidate.containedElements[i].id);
+
+ if (element === undefined) {
+ element = optimizationCandidate.containedElements[i];
+ }
+ console.log(element);
+
// for sequence flows check the position of each waypoint and label
if (element.type === "bpmn:SequenceFlow") {
if (element.waypoints) {
@@ -314,6 +337,7 @@ function findEntryPoints(rootElement) {
*/
function getXOREntryPoints(rootElement) {
let entryPoints = [];
+ console.log("find xor entry point ", rootElement);
// search for XOR gateways within the workflow
const flowElements = rootElement.flowElements;
@@ -324,6 +348,8 @@ function getXOREntryPoints(rootElement) {
// the gateway should have exactly one outgoing flow representing the control flow of the hybrid loop
if (flowElement.outgoing.length === 1) {
+ flowElement.$parent = rootElement;
+ console.log(flowElement.$parent);
console.log(
"Exclusive gateway is potential entry point: ",
flowElement
@@ -331,6 +357,13 @@ function getXOREntryPoints(rootElement) {
entryPoints.push(flowElement);
}
}
+
+ if (flowElement.$type && flowElement.$type === "bpmn:SubProcess") {
+ console.log("Found subprocess ", flowElement);
+ Array.prototype.push.apply(entryPoints, getXOREntryPoints(flowElement));
+
+ // recursively call method to find optimization candidates
+ }
}
return entryPoints;
@@ -468,9 +501,10 @@ function containsClassicalTask(candidate) {
for (let i = 0; i < candidate.containedElements.length; i++) {
let element = candidate.containedElements[i];
if (
- element.$type &&
- (element.$type === "bpmn:ServiceTask" ||
- element.$type === "bpmn:ScriptTask")
+ (element.$type &&
+ (element.$type === "bpmn:ServiceTask" ||
+ element.$type === "bpmn:ScriptTask")) ||
+ element.$type.startsWith("quantme:")
) {
return true;
}
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/QiskitRuntimeHandler.js b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/QiskitRuntimeHandler.js
index 6774caaa..ec45859e 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/QiskitRuntimeHandler.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/QiskitRuntimeHandler.js
@@ -19,14 +19,16 @@ import {
createNewArtifactTemplate,
createNewServiceTemplateVersion,
} from "../../../../opentosca/deployment/OpenTOSCAUtils";
-import {
- getInvalidModelingConstruct,
- getRequiredPrograms,
- getTaskOrder,
-} from "./RuntimeHandlerUtils";
+import { getRequiredPrograms, getTaskOrder } from "./RuntimeHandlerUtils";
import { getXml } from "../../../../../editor/util/IoUtilities";
import { createTempModelerFromXml } from "../../../../../editor/ModelerHandler";
import { getRootProcess } from "../../../../../editor/util/ModellingUtilities";
+import { getWineryEndpoint } from "../../../../opentosca/framework-config/config-manager";
+import {
+ getHybridRuntimeProvenance,
+ getQiskitRuntimeHandlerEndpoint,
+} from "../../../framework-config/config-manager";
+import JSZip from "jszip";
/**
* Generate a Qiskit Runtime program for the given candidate
@@ -57,8 +59,10 @@ export async function getQiskitRuntimeProgramDeploymentModel(
};
}
}
-
+ console.log(candidate);
let xml = await getXml(candidate.modeler);
+ console.log("xmlQiskit");
+ console.log(xml);
// transform QuantME tasks within candidate
let transformationResult = await startQuantmeReplacementProcess(
@@ -73,13 +77,18 @@ export async function getQiskitRuntimeProgramDeploymentModel(
"Unable to transform QuantME tasks within the candidates. Please provide valid QRMs!",
};
}
-
+ console.log(transformationResult);
// import transformed XML to the modeler
let modeler = await createTempModelerFromXml(transformationResult.xml);
let rootElement = getRootProcess(modeler.getDefinitions());
// check if transformed XML contains invalid modeling constructs
- let invalidModelingConstruct = getInvalidModelingConstruct(rootElement);
+ let elementRegistry = modeler.get("elementRegistry");
+ rootElement = elementRegistry.get(rootElement.id);
+ console.log(rootElement);
+ let invalidModelingConstruct = undefined;
+ //getInvalidModelingConstruct(rootElement);
+ console.log(invalidModelingConstruct);
if (invalidModelingConstruct !== undefined) {
console.log(
"Found invalid modeling construct of type: ",
@@ -92,21 +101,29 @@ export async function getQiskitRuntimeProgramDeploymentModel(
};
}
+ let wineryEndpoint = getWineryEndpoint();
+ let requiredProgramsZip = new JSZip();
// check if all service tasks have either a deployment model attached and all script tasks provide the code inline and retrieve the files
let requiredPrograms = await getRequiredPrograms(
rootElement,
- modelerConfig.wineryEndpoint
+ requiredProgramsZip,
+ wineryEndpoint
);
+ console.log(requiredPrograms);
if (requiredPrograms.error !== undefined) {
return { error: requiredPrograms.error };
}
+ let qiskitRuntimeHandlerEndpoint = getQiskitRuntimeHandlerEndpoint();
+ console.log(qiskitRuntimeHandlerEndpoint);
+ let hybridRuntimeProvenance = getHybridRuntimeProvenance();
+
// invoke handler and return resulting hybrid program or error message
let runtimeGenerationResult = await invokeQiskitRuntimeHandler(
candidate,
requiredPrograms,
- modelerConfig.qiskitRuntimeHandlerEndpoint,
- modelerConfig.hybridRuntimeProvenance,
+ qiskitRuntimeHandlerEndpoint,
+ hybridRuntimeProvenance,
modeler
);
if (runtimeGenerationResult.error !== undefined) {
@@ -117,7 +134,7 @@ export async function getQiskitRuntimeProgramDeploymentModel(
let deploymentModelUrl = await createDeploymentModel(
candidate,
runtimeGenerationResult,
- modelerConfig.wineryEndpoint
+ wineryEndpoint
);
if (deploymentModelUrl.error !== undefined) {
return { error: deploymentModelUrl.error };
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/RuntimeHandlerUtils.js b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/RuntimeHandlerUtils.js
index a670aabc..93f085d7 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/RuntimeHandlerUtils.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/runtimes/RuntimeHandlerUtils.js
@@ -26,7 +26,17 @@ export function getTaskOrder(candidate, modeler) {
// get entry point from the current modeler
let elementRegistry = modeler.get("elementRegistry");
- let element = elementRegistry.get(candidate.entryPoint.id).businessObject;
+ console.log(candidate);
+ console.log(candidate.entryPoint);
+ console.log(modeler);
+ console.log(elementRegistry);
+ console.log(elementRegistry.get(candidate.entryPoint.id));
+ let element = candidate.entryPoint;
+ if (elementRegistry.get(candidate.entryPoint.id) !== undefined) {
+ element = elementRegistry.get(candidate.entryPoint.id).businessObject;
+ }
+ console.log(element);
+ console.log(candidate.entryPoint);
// search all tasks before looping gateway
while (element.id !== candidate.exitPoint.id) {
@@ -37,6 +47,7 @@ export function getTaskOrder(candidate, modeler) {
beforeLoop.push(element.id);
}
+ console.log(element);
// get next element
element = getNextElement(element);
}
@@ -49,7 +60,7 @@ export function getTaskOrder(candidate, modeler) {
) {
afterLoop.push(element.id);
}
-
+ console.log(element);
// get next element
element = getNextElement(element);
}
@@ -67,6 +78,14 @@ function getNextElement(element) {
if (element.$type === "bpmn:SequenceFlow") {
return element.targetRef;
} else {
+ if (element.$type === "bpmn:ExclusiveGateway") {
+ let outgoingflow = element.outgoing[0];
+ console.log(outgoingflow);
+ if (outgoingflow.targetRef.$type === "bpmn:EndEvent") {
+ outgoingflow = element.outgoing[1];
+ }
+ return outgoingflow;
+ }
return element.outgoing[0];
}
}
@@ -78,15 +97,38 @@ function getNextElement(element) {
* @param wineryEndpoint the endpoint of a Winery to load the required deployment models from
* @return the list of all retrieved files or an error message if the retrieval fails
*/
-export async function getRequiredPrograms(rootElement, wineryEndpoint) {
- let requiredProgramsZip = new JSZip();
+export async function getRequiredPrograms(
+ rootElement,
+ requiredProgramsZip,
+ wineryEndpoint
+) {
+ console.log("Get required programs...");
- for (let i = 0; i < rootElement.flowElements.length; i++) {
- let element = rootElement.flowElements[i];
+ let flowElements = rootElement.flowElements;
+ console.log(flowElements);
+ if (flowElements === undefined) {
+ flowElements = rootElement.children;
+ }
+ console.log(flowElements);
+ for (let i = 0; i < flowElements.length; i++) {
+ let element = flowElements[i];
+ console.log(element);
+ // if elements are inside a subprocess
+ if (
+ element.$type === "bpmn:SubProcess" ||
+ element.type === "bpmn:SubProcess"
+ ) {
+ console.log("Found subprocess:", element.id);
+ await getRequiredPrograms(element, requiredProgramsZip, wineryEndpoint);
+ }
// service task needs attached deployment model that is accessible through the defined URL
- if (element.$type === "bpmn:ServiceTask") {
- if (element.deploymentModelUrl === undefined) {
+ if (
+ element.$type === "bpmn:ServiceTask" ||
+ element.type === "bpmn:ServiceTask"
+ ) {
+ console.log(element);
+ if (element.businessObject.deploymentModelUrl === undefined) {
console.log(
"No deployment model defined for ServiceTask: ",
element.id
@@ -97,22 +139,35 @@ export async function getRequiredPrograms(rootElement, wineryEndpoint) {
}
// replace generic placeholder by endpoint of connected Winery
- let url = element.deploymentModelUrl.replace(
+ let url = element.businessObject.deploymentModelUrl.replace(
"{{ wineryEndpoint }}",
wineryEndpoint
);
// download the deployment model from the given URL
console.log("Retrieving deployment model from URL: ", url);
- const response = await fetch(url);
+ const response = await fetch(url, {
+ headers: {
+ Accept: "application/zip",
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+
console.log(response);
+ console.log(response.headers);
const blob = await response.blob();
+ console.log(blob);
// unzip the retrieved CSAR
let zip = await new JSZip().loadAsync(blob);
+ console.log("zip");
// get all contained deployment artifacts
let files = getDeploymentArtifactFiles(zip);
+ console.log(files);
// only one deployment artifact is allowed containing the quantum and classical programs
if (files.length !== 1) {
@@ -182,8 +237,13 @@ export async function getRequiredPrograms(rootElement, wineryEndpoint) {
* @return the first invalid modeling construct or undefined if all are valid
*/
export function getInvalidModelingConstruct(rootElement) {
- for (let i = 0; i < rootElement.flowElements.length; i++) {
- let element = rootElement.flowElements[i];
+ console.log(rootElement);
+ let flowElements = rootElement.flowElements;
+ if (rootElement.flowElements === undefined) {
+ flowElements = rootElement.children;
+ }
+ for (let i = 0; i < flowElements.length; i++) {
+ let element = flowElements[i];
if (
element.$type !== "bpmn:ExclusiveGateway" &&
element.$type !== "bpmn:SequenceFlow" &&
diff --git a/components/bpmn-q/test/tests/editor/plugin.spec.js b/components/bpmn-q/test/tests/editor/plugin.spec.js
index 3ec3b32a..1ed5a6be 100644
--- a/components/bpmn-q/test/tests/editor/plugin.spec.js
+++ b/components/bpmn-q/test/tests/editor/plugin.spec.js
@@ -22,17 +22,18 @@ describe("Test plugins", function () {
expect(getActivePlugins().length).to.equal(0);
});
- it("Should find 4 active plugins", function () {
+ it("Should find 5 active plugins", function () {
setPluginConfig([
{ name: pluginNames.DATAFLOW },
{ name: pluginNames.QUANTME },
{ name: pluginNames.OPENTOSCA },
{ name: pluginNames.PLANQK },
+ { name: pluginNames.PATTERN },
]);
const plugins = getActivePlugins();
- expect(plugins.length).to.equal(4);
+ expect(plugins.length).to.equal(5);
expect(
plugins.filter((plugin) => plugin.name === pluginNames.DATAFLOW)
.length
@@ -47,29 +48,36 @@ describe("Test plugins", function () {
expect(
plugins.filter((plugin) => plugin.name === pluginNames.PLANQK).length
).to.equal(1);
+ expect(
+ plugins.filter((plugin) => plugin.name === pluginNames.PATTERN).length
+ ).to.equal(1);
});
- it("Should find 4 active plugins due to dependencies", function () {
+ it("Should find 5 active plugins due to dependencies", function () {
setPluginConfig([
{ name: pluginNames.DATAFLOW },
- { name: pluginNames.QUANTME },
+ { name: pluginNames.PATTERN },
{ name: pluginNames.PLANQK },
]);
const plugins = getActivePlugins();
- expect(plugins.length).to.equal(4);
+ expect(plugins.length).to.equal(5);
expect(
plugins.filter((plugin) => plugin.name === pluginNames.DATAFLOW)
.length
).to.equal(1);
expect(
- plugins.filter((plugin) => plugin.name === pluginNames.QUANTME).length
+ plugins.filter((plugin) => plugin.name === pluginNames.PATTERN).length
).to.equal(1);
expect(
plugins.filter((plugin) => plugin.name === pluginNames.PLANQK).length
).to.equal(1);
+ // should be found due to pattern plugin
+ expect(
+ plugins.filter((plugin) => plugin.name === pluginNames.QUANTME).length
+ ).to.equal(1);
// should be found due to dependency of quantme plugin
expect(
plugins.filter((plugin) => plugin.name === pluginNames.OPENTOSCA)
@@ -85,16 +93,18 @@ describe("Test plugins", function () {
{ name: pluginNames.QUANTME },
{ name: pluginNames.OPENTOSCA },
{ name: pluginNames.PLANQK },
+ { name: pluginNames.PATTERN },
]);
const modules = getAdditionalModules();
const extensions = getModdleExtension();
- expect(modules.length).to.equal(4);
+ expect(modules.length).to.equal(5);
expect(extensions[pluginNames.DATAFLOW]).to.not.be.undefined;
expect(extensions[pluginNames.QUANTME]).to.not.be.undefined;
expect(extensions[pluginNames.OPENTOSCA]).to.not.be.undefined;
expect(extensions[pluginNames.PLANQK]).to.not.be.undefined;
+ expect(extensions[pluginNames.PLANQK]).to.not.be.undefined;
});
});
});
diff --git a/components/bpmn-q/test/tests/helpers/DiagramHelper.js b/components/bpmn-q/test/tests/helpers/DiagramHelper.js
index 3602efbf..1c1025a3 100644
--- a/components/bpmn-q/test/tests/helpers/DiagramHelper.js
+++ b/components/bpmn-q/test/tests/helpers/DiagramHelper.js
@@ -349,4 +349,8 @@ export const validDataFlowDiagram =
export const validQuantMESubprocessDiagram =
'
Flow_036tksd Flow_19sigyl Flow_036tksdFlow_0k45o2vdef packagesUrl = execution.getVariable("packagesUrl"); def packageString = new URL (packagesUrl).getText(); //def packageString = new URL ("https://raw.githubusercontent.com/UST-QuAntiL/QuantME-UseCases/icwe/2023-icwe/data/packages.txt").getText(); def packages = [] def destinations = [] packageString.split("\\n").each { p -> def packageValues = [:] def values = p.split(",") packageValues.put("destination", values[0]) packageValues.put("size", values[1].toInteger()) packageValues.put("deliveryDate", values[2]) packages.add(packageValues) if (!destinations.contains(values[0])){destinations.add(values[0]) } } println(packages); println(destinations) execution.setVariable("destinations", destinations); execution.setVariable("packages", packages); execution.setVariable("nextDestinations", [destinations.getClass().newInstance(destinations)]); Flow_0k45o2vFlow_1o0job9 def trucksUrl = execution.getVariable("trucksUrl"); def destinations = execution.getVariable("destinations"); def trucksString = new URL (trucksUrl).getText(); def trucks = [] trucksString.split("\\n").each { p -> def truckValues = [:] def values = p.split(",") truckValues.put("driver", values[0]) truckValues.put("capacity", values[1].toInteger()) truckValues.put("location", values[2]) truckValues.put("email", values[3]) trucks.add(truckValues) if (!destinations.contains(values[2])){destinations.add(values[2]) } } execution.setVariable("trucks", trucks); execution.setVariable("allCities", destinations); execution.setVariable("unassignedTrucks", trucks); POST application/jsonapplication/json http://distance-matrix:8101/useCaseDistanceMatrix import groovy.json.JsonBuilderdef allCities = execution.getVariable("allCities"); def aws_token = execution.getVariable("awsToken"); def request = [:]; request.put("towns", allCities); request.put("token", aws_token); requeststring = new JsonBuilder(request).toPrettyString() return requeststring; def resp = connector.getVariable("response");resp = new groovy.json.JsonSlurper().parseText(resp)distanceMatrix= resp.get("distanceMatrix")println(distanceMatrix);return distanceMatrix; def resp = connector.getVariable("response");resp = new groovy.json.JsonSlurper().parseText(resp)durationMatrix= resp.get("durationMatrix")println(durationMatrix);return durationMatrix; http-connectorFlow_1o0job9Flow_0pqisjm Flow_164r496Flow_1uk996u def nextDestinations = execution.getVariable("nextDestinations"); execution.setVariable("currentDestinations", nextDestinations[0].getClass().newInstance(nextDestinations[0])); Flow_1lgu2v0Flow_19sigyl Flow_1uk996uFlow_0ey4t8g def unassignedTrucks = execution.getVariable("unassignedTrucks"); def packages = execution.getVariable("packages"); def currentDestinations = execution.getVariable("currentDestinations"); def maxCapacity = 0; unassignedTrucks.each { truck -> if (truck.get("capacity") > maxCapacity) {maxCapacity = truck.get("capacity"); } } def totalSize = 0; packages.each { p -> if( currentDestinations.contains(p.get("destination"))) {totalSize += p.get("size"); } } execution.setVariable("allFitsInTruck", totalSize < maxCapacity); Flow_08svt2nFlow_1lgu2v0Flow_1mglfkn def unassignedTrucks = execution.getVariable("unassignedTrucks"); def nextDestinations= execution.getVariable("nextDestinations"); return !(unassignedTrucks.size() > 0 && nextDestinations.size() > 0) Flow_1mglfknFlow_0kiekyoFlow_03ixpie def unassignedTrucks = execution.getVariable("unassignedTrucks"); def nextDestinations= execution.getVariable("nextDestinations"); return (unassignedTrucks.size() > 0 && nextDestinations.size() > 0) Flow_0pqisjmFlow_03ixpieFlow_164r496 Flow_0ey4t8gFlow_0wg476aFlow_1d4o5uw ${ execution.getVariable("allFitsInTruck")!= null && execution.getVariable("allFitsInTruck") == "true"} Flow_0wg476aFlow_1k013oeFlow_1m1hkm4 Flow_1d4o5uwFlow_0awxqtpFlow_1k013oe ${ execution.getVariable("allFitsInTruck")== null || execution.getVariable("allFitsInTruck") == "false"} def unassignedTrucks = execution.getVariable("unassignedTrucks"); def currentDestinations = execution.getVariable("currentDestinations"); return unassignedTrucks.size() > 1 && currentDestinations.size() > 1 def unassignedTrucks = execution.getVariable("unassignedTrucks"); def currentDestinations = execution.getVariable("currentDestinations"); return !(unassignedTrucks.size() > 1 && currentDestinations.size() > 1) Flow_0t1933oFlow_08svt2ndef unassignedTrucks = execution.getVariable("unassignedTrucks");def nextDestinations = execution.getVariable("nextDestinations");def currentRoute= execution.getVariable("currentRoute");def allRoutes= execution.getVariable("allRoutes");println(unassignedTrucks[0].get("driver")+" on route" + currentRoute.inspect());if(allRoutes == null){ allRoutes = [];}this_route =[:]this_route.put("route", currentRoute.getClass().newInstance(currentRoute));this_route.put("driver", unassignedTrucks[0].getClass().newInstance(unassignedTrucks[0]));allRoutes.push(this_route);nextDestinations.removeAt(0);unassignedTrucks.removeAt(0);execution.setVariable("allRoutes", allRoutes);execution.setVariable("nextDestinations", nextDestinations);execution.setVariable("unassignedTrucks", unassignedTrucks); Flow_1gu7ma8Flow_0kiekyo def currentDestinations = execution.getVariable("currentDestinations"); def nextDestinations = execution.getVariable("nextDestinations"); def evaluatedCosts = execution.getVariable("evaluatedCosts");println(nextDestinations); evaluatedCosts = evaluatedCosts[0].get("bitstring");println(evaluatedCosts); def cities_with_zero =[]; def cities_with_one =[]; evaluatedCosts.toCharArray().eachWithIndex { c, index -> (c == "0") ? cities_with_zero.push(currentDestinations[index]) : cities_with_one.push(currentDestinations[index]); }if (cities_with_zero.size()==0 || cities_with_one.size()==0) { nextDestinations.push(currentDestinations[0..((int)(currentDestinations.size()/2))-1]); nextDestinations.push(currentDestinations[((int)(currentDestinations.size()/2))..currentDestinations.size()-1]);} else { nextDestinations.push(cities_with_zero); nextDestinations.push(cities_with_one);}println(nextDestinations); nextDestinations.removeAt(0); println(nextDestinations); execution.setVariable("nextDestinations", nextDestinations); Flow_1m1hkm4Flow_0t1933o [1] [1] YOUR_TOKENFlow_12pjp7kFlow_1gu7ma8 Flow_0awxqtpFlow_12pjp7k def allCities = execution.getVariable("allCities"); def currentDestinations = execution.getVariable("currentDestinations"); def distanceMatrix = execution.getVariable("distanceMatrix"); def durationMatrix = execution.getVariable("durationMatrix"); def requiredIndizes = [] for (def i in 0..allCities.size()-1) { if (currentDestinations.contains(allCities[i])){requiredIndizes.push(i) } } def submatrixOfDistanceMatrix = new Integer [requiredIndizes.size()] [requiredIndizes.size()]; def submatrixOfDurationMatrix = new Float [requiredIndizes.size()] [requiredIndizes.size()]; for (def i in 0..requiredIndizes.size()-1) { submatrixOfDistanceMatrix [i][i] = 0; submatrixOfDurationMatrix [i][i] = 0.0; for (def j in i+1..requiredIndizes.size()-1) {if (j < requiredIndizes.size()){submatrixOfDistanceMatrix [i][j] = distanceMatrix[requiredIndizes[i]][requiredIndizes[j]];submatrixOfDistanceMatrix [j][i] = distanceMatrix[requiredIndizes[j]][requiredIndizes[i]];submatrixOfDurationMatrix [j][i] = durationMatrix[requiredIndizes[j]][requiredIndizes[i]];submatrixOfDurationMatrix [j][i] = durationMatrix[requiredIndizes[j]][requiredIndizes[i]];} } }println(submatrixOfDistanceMatrix); // switch between distance and duration depending on reqs execution.setVariable("adjMatrix", submatrixOfDistanceMatrix); ';
+
+export const validPatternDiagram =
+ '
Flow_1xhrbpv Flow_1xhrbpv Flow_0xn9oed Flow_1tfrxew Flow_1tfrxew Flow_13c71gm Flow_13c71gm Flow_1mo76yx SequenceFlow_14lmcjd SequenceFlow_0q54ilk SequenceFlow_1g4nyfq SequenceFlow_0rvah9x SequenceFlow_03d0zlb SequenceFlow_14lmcjd SequenceFlow_123mbe9 ${ execution.getVariable("converged")!= null && execution.getVariable("converged") == "true"} ${ execution.getVariable("converged")== null || execution.getVariable("converged") == "false"} SequenceFlow_0rvah9x SequenceFlow_03d0zlb SequenceFlow_123mbe9 SequenceFlow_0wggqgf SequenceFlow_0q54ilk SequenceFlow_0wggqgf SequenceFlow_1g4nyfq Flow_1mo76yx Flow_0tbeqf7 Flow_0tbeqf7 Flow_0xn9oed ';
+
export { validPlanqkDiagram, transformedValidPlanqkDiagram };
diff --git a/components/bpmn-q/test/tests/pattern/pattern-config.spec.js b/components/bpmn-q/test/tests/pattern/pattern-config.spec.js
new file mode 100644
index 00000000..a5d9f80a
--- /dev/null
+++ b/components/bpmn-q/test/tests/pattern/pattern-config.spec.js
@@ -0,0 +1,37 @@
+import { setPluginConfig } from "../../../modeler-component/editor/plugin/PluginConfigHandler";
+import { expect } from "chai";
+import * as patternConfig from "../../../modeler-component/extensions/pattern/framework-config/config-manager";
+
+describe("Test Pattern ConfigManager", function () {
+ describe("Test Pattern endpoint", function () {
+ before("Reset Pattern configuration", function () {
+ patternConfig.resetConfig();
+ });
+
+ afterEach("Reset Pattern configuration", function () {
+ patternConfig.resetConfig();
+ });
+
+ it("Should configure Pattern endpoints", function () {
+ setPluginConfig([
+ {
+ name: "pattern",
+ config: {
+ patternAtlasEndpoint:
+ "http://test:1977/patternatlas/patternLanguages/af7780d5-1f97-4536-8da7-4194b093ab1d",
+ patternAtlasUIEndpoint: "http://test:1978",
+ qcAtlasEndpoint: "http://test:6626",
+ },
+ },
+ ]);
+
+ expect(patternConfig.getPatternAtlasEndpoint()).to.equal(
+ "http://test:1977/patternatlas/patternLanguages/af7780d5-1f97-4536-8da7-4194b093ab1d"
+ );
+ expect(patternConfig.getPatternAtlasUIEndpoint()).to.equal(
+ "http://test:1978"
+ );
+ expect(patternConfig.getQcAtlasEndpoint()).to.equal("http://test:6626");
+ });
+ });
+});
diff --git a/components/bpmn-q/test/tests/pattern/pattern-transformation.spec.js b/components/bpmn-q/test/tests/pattern/pattern-transformation.spec.js
new file mode 100644
index 00000000..0fdd4fee
--- /dev/null
+++ b/components/bpmn-q/test/tests/pattern/pattern-transformation.spec.js
@@ -0,0 +1,79 @@
+const { validPatternDiagram } = require("../helpers/DiagramHelper");
+const chai = require("chai");
+const {
+ WARM_STARTING_TASK,
+ QUANTUM_CIRCUIT_LOADING_TASK,
+ CIRCUIT_CUTTING_TASK,
+ CUTTING_RESULT_COMBINATION_TASK,
+ READOUT_ERROR_MITIGATION_TASK,
+ RESULT_EVALUATION_TASK,
+ QUANTUM_CIRCUIT_EXECUTION_TASK,
+} = require("../../../modeler-component/extensions/quantme/Constants");
+const {
+ startPatternReplacementProcess,
+} = require("../../../modeler-component/extensions/pattern/replacement/PatternTransformator");
+const { instantiateModeler } = require("../helpers/ModelerHelper");
+describe("Test the PatternTransformator of the Pattern extension.", function () {
+ describe("Transformation of Pattern extensions", function () {
+ it("should replace all patterns by quantme modeling constructs", async function () {
+ const transformationResult = await startPatternReplacementProcess(
+ validPatternDiagram
+ );
+
+ chai.expect(transformationResult.status).to.equal("transformed");
+ chai
+ .expect(transformationResult.xml)
+ .to.contain("