From 5e828afcd6998342f4c3631324f35042112c7f23 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:06:15 +0100 Subject: [PATCH 1/9] Update .pre-commit-config.yaml --- .pre-commit-config.yaml | 194 +++++++++++++++++++--------------------- pyproject.toml | 1 + 2 files changed, 92 insertions(+), 103 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9dc06521..573c69f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,97 +1,139 @@ # To run all pre-commit checks, use: # -# pre-commit run -a +# uvx prek run -a # # To install pre-commit hooks that run every time you commit: # -# pre-commit install +# uv tool install prek +# prek install # ci: autoupdate_commit_msg: "⬆️🪝 update pre-commit hooks" - autofix_commit_msg: "🎨 pre-commit fixes" autoupdate_schedule: quarterly + autofix_commit_msg: "🎨 pre-commit fixes" skip: [mypy] repos: - # Standard hooks + # Priority 0: Fast validation and independent fixers + + ## Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - - id: check-added-large-files - args: ["--maxkb=2048"] - - id: check-case-conflict - - id: check-vcs-permalinks - id: check-merge-conflict - - id: check-symlinks - - id: check-json - - id: check-toml - - id: check-yaml - - id: debug-statements + priority: 0 - id: end-of-file-fixer - - id: mixed-line-ending + priority: 1 - id: trailing-whitespace + priority: 1 - # Clean jupyter notebooks - - repo: https://github.com/srstevenson/nb-clean - rev: 4.0.1 + ## Check the pyproject.toml file + - repo: https://github.com/henryiii/validate-pyproject-schema-store + rev: 2026.01.22 hooks: - - id: nb-clean - args: - - --remove-empty-cells - - --preserve-cell-metadata - - raw_mimetype - - -- - - # Handling unwanted unicode characters - - repo: https://github.com/sirosen/texthooks - rev: 0.7.1 + - id: validate-pyproject + priority: 0 + + ## Check JSON schemata + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.36.1 hooks: - - id: fix-ligatures - - id: fix-smartquotes + - id: check-github-workflows + priority: 0 + - id: check-readthedocs + priority: 0 - # Check for common mistakes - - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.10.0 + ## Catch common capitalization mistakes + - repo: local hooks: - - id: rst-backticks - - id: rst-directive-colons - - id: rst-inline-touching-normal + - id: disallow-caps + name: Disallow improper capitalization + language: pygrep + entry: Nanobind|Numpy|Github|PyTest|Mqt|Tum + exclude: .pre-commit-config.yaml + priority: 0 + + ## Check for spelling + - repo: https://github.com/adhtruong/mirrors-typos + rev: v1.42.3 + hooks: + - id: typos + priority: 0 + + ## Check best practices for scientific Python code + - repo: https://github.com/scientific-python/cookie + rev: 2025.11.21 + hooks: + - id: sp-repo-review + additional_dependencies: ["repo-review[cli]"] + priority: 0 - # Check for license headers + ## Check for license headers - repo: https://github.com/emzeat/mz-lictools rev: v2.9.0 hooks: - id: license-tools + priority: 0 - # Ensure uv lock file is up-to-date + ## Ensure uv lock file is up-to-date - repo: https://github.com/astral-sh/uv-pre-commit rev: 0.9.28 hooks: - id: uv-lock + priority: 0 - # Python linting and formatting using ruff + ## Tidy up BibTeX files + - repo: https://github.com/FlamingTempura/bibtex-tidy + rev: v1.14.0 + hooks: + - id: bibtex-tidy + args: + [ + "--align=20", + "--curly", + "--months", + "--blank-lines", + "--sort", + "--strip-enclosing-braces", + "--sort-fields", + "--trailing-commas", + "--remove-empty-fields", + ] + priority: 0 + + # Priority 1: Second-pass fixers + + ## Format configuration files with prettier + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.8.1 + hooks: + - id: prettier + types_or: [yaml, markdown, html, css, scss, javascript, json] + priority: 1 + + ## Python linting using ruff - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.14 hooks: - - id: ruff-check - id: ruff-format + priority: 1 + - id: ruff-check + require_serial: true + priority: 2 - # Also run Black on examples in the documentation + # Priority 2+: Final checks and fixers + + ## Also run Black on examples in the documentation (needs to run after ruff format) - repo: https://github.com/adamchainz/blacken-docs rev: 1.20.0 hooks: - id: blacken-docs - additional_dependencies: [black==25.*] - - # Format configuration files with prettier - - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.8.1 - hooks: - - id: prettier - types_or: [yaml, markdown, html, css, scss, javascript, json] + language: python + additional_dependencies: [black==26.*] + priority: 2 - # Check static types with mypy + ## Static type checking using mypy (needs to run after lockfile update/ruff format, and ruff lint) - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.19.1 hooks: @@ -106,57 +148,3 @@ repos: - pandas-stubs - mqt-core - mqt-bench>=2.0.1 - - # Check for spelling - - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.42.3 - hooks: - - id: typos - - # Catch common capitalization mistakes - - repo: local - hooks: - - id: disallow-caps - name: Disallow improper capitalization - language: pygrep - entry: PyBind|Numpy|Cmake|CCache|Github|PyTest|Mqt|Tum - exclude: .pre-commit-config.yaml|\.pkl$|\.zip$|\.json$ - - # Check best practices for scientific Python code - - repo: https://github.com/scientific-python/cookie - rev: 2025.11.21 - hooks: - - id: sp-repo-review - additional_dependencies: ["repo-review[cli]"] - - # Check JSON schemata - - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.36.1 - hooks: - - id: check-dependabot - - id: check-github-workflows - - id: check-readthedocs - - # Check the pyproject.toml file - - repo: https://github.com/henryiii/validate-pyproject-schema-store - rev: 2026.01.22 - hooks: - - id: validate-pyproject - - # Tidy up BibTeX files - - repo: https://github.com/FlamingTempura/bibtex-tidy - rev: v1.14.0 - hooks: - - id: bibtex-tidy - args: - [ - "--align=20", - "--curly", - "--months", - "--blank-lines", - "--sort", - "--strip-enclosing-braces", - "--sort-fields", - "--trailing-commas", - "--remove-empty-fields", - ] diff --git a/pyproject.toml b/pyproject.toml index dbcfc079..485d570d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -261,6 +261,7 @@ aer = "aer" ignore = [ "GH200", # We use Renovate instead of Dependabot "PC160", # We use a mirror of crate-ci/typos + "PC170", # We do not use rST files anymore ] From 30a1c7f5a49a02308635114d9292acbc7afff2cd Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:06:42 +0100 Subject: [PATCH 2/9] Use newer runners --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40a3b72e..ce87b596 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: strategy: fail-fast: false matrix: - runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-14, windows-2022] + runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, windows-2025] uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11 with: runs-on: ${{ matrix.runs-on }} From 8c8066116542a481eca277782d6e8bb228c11116 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:08:25 +0100 Subject: [PATCH 3/9] Use ubuntu-slim instead of ubuntu-latest --- .github/workflows/ci.yml | 2 +- .github/workflows/release-drafter.yml | 2 +- .github/workflows/templating.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce87b596..ede20e9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - python-linter - build-sdist - build-wheel - runs-on: ubuntu-latest + runs-on: ubuntu-slim steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 8389020f..a31a55d5 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: write pull-requests: write - runs-on: ubuntu-latest + runs-on: ubuntu-slim steps: - uses: release-drafter/release-drafter@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0 env: diff --git a/.github/workflows/templating.yml b/.github/workflows/templating.yml index 690798da..43fbb18d 100644 --- a/.github/workflows/templating.yml +++ b/.github/workflows/templating.yml @@ -8,7 +8,7 @@ on: jobs: render-template: name: Render template - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write pull-requests: write From 72d5c9efd30700381f585d51a927d65da8bc7664 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:14:45 +0100 Subject: [PATCH 4/9] Add back nb-clean --- .pre-commit-config.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 573c69f2..98ecaea4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -124,6 +124,18 @@ repos: # Priority 2+: Final checks and fixers + ## Clean Jupyter notebooks + - repo: https://github.com/srstevenson/nb-clean + rev: 4.0.1 + hooks: + - id: nb-clean + args: + - --remove-empty-cells + - --preserve-cell-metadata + - raw_mimetype + - -- + priority: 2 + ## Also run Black on examples in the documentation (needs to run after ruff format) - repo: https://github.com/adamchainz/blacken-docs rev: 1.20.0 From b5f6304bbe928c157cea920cd2104bd5e4205f58 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:37:35 +0100 Subject: [PATCH 5/9] Set priority for mypy --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 98ecaea4..d2c7ee41 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: - id: disallow-caps name: Disallow improper capitalization language: pygrep - entry: Nanobind|Numpy|Github|PyTest|Mqt|Tum + entry: Numpy|Github|PyTest|Mqt|Tum exclude: .pre-commit-config.yaml priority: 0 @@ -160,3 +160,4 @@ repos: - pandas-stubs - mqt-core - mqt-bench>=2.0.1 + priority: 3 From 767ec050e5a34b80a73a6f15b3f4205c50adaf60 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:42:42 +0100 Subject: [PATCH 6/9] Format TypeScript files with prettier --- .pre-commit-config.yaml | 4 +- webpage/app/edgeCollector.tsx | 141 ++++---- webpage/app/edgeDisplay.tsx | 32 +- webpage/app/graphView.tsx | 227 +++++++------ webpage/app/infoScreen.tsx | 44 ++- webpage/app/layout.tsx | 20 +- webpage/app/legal/page.tsx | 234 +++++++++---- webpage/app/page.tsx | 292 +++++++++++----- webpage/app/pathPosIs.tsx | 41 ++- webpage/app/pathPosIsCollector.tsx | 175 +++++----- webpage/app/settings.ts | 521 ++++++++++++++++------------- webpage/app/titledTextbox.tsx | 15 +- webpage/app/toggle.tsx | 41 +-- webpage/app/toggleBag.tsx | 109 +++--- webpage/types/react-graph.vis.d.ts | 4 +- 15 files changed, 1142 insertions(+), 758 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d2c7ee41..48e35f27 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,7 @@ repos: name: Disallow improper capitalization language: pygrep entry: Numpy|Github|PyTest|Mqt|Tum - exclude: .pre-commit-config.yaml + exclude: ^(.pre-commit-config.yaml|webpage/package-lock.json|.*.zip) priority: 0 ## Check for spelling @@ -109,7 +109,7 @@ repos: rev: v3.8.1 hooks: - id: prettier - types_or: [yaml, markdown, html, css, scss, javascript, json] + types_or: [yaml, markdown, html, css, scss, javascript, json, ts, tsx] priority: 1 ## Python linting using ruff diff --git a/webpage/app/edgeCollector.tsx b/webpage/app/edgeCollector.tsx index 3fff6b52..25c0b488 100644 --- a/webpage/app/edgeCollector.tsx +++ b/webpage/app/edgeCollector.tsx @@ -1,76 +1,87 @@ -import React, { useState } from 'react'; -import Toggle from './toggle'; -import EdgeDisplay from './edgeDisplay'; +import React, { useState } from "react"; +import Toggle from "./toggle"; +import EdgeDisplay from "./edgeDisplay"; interface EdgeCollectorProps { - title: string; - cols?: number; - allVertices: string[]; - onChange?: (edges: string[][]) => void; + title: string; + cols?: number; + allVertices: string[]; + onChange?: (edges: string[][]) => void; } -const EdgeCollector: React.FC = ({ title, cols = 4, allVertices, onChange}) => { - // Component logic goes here - const [items, setItems] = useState([]); +const EdgeCollector: React.FC = ({ + title, + cols = 4, + allVertices, + onChange, +}) => { + // Component logic goes here + const [items, setItems] = useState([]); - const itemAdded = (fromVertex: string, toVertex: string) => { - if(fromVertex === "" || toVertex === "") - return; - if(items.some((item) => item[0] == fromVertex && item[1] == toVertex)) - return; - const newItems = [...items]; - newItems.push([fromVertex, toVertex]); - setItems(newItems); - onChange?.(newItems); - } + const itemAdded = (fromVertex: string, toVertex: string) => { + if (fromVertex === "" || toVertex === "") return; + if (items.some((item) => item[0] == fromVertex && item[1] == toVertex)) + return; + const newItems = [...items]; + newItems.push([fromVertex, toVertex]); + setItems(newItems); + onChange?.(newItems); + }; - const itemRemoved = (fromVertex: string, toVertex: string) => { - const newItems = [...items]; - newItems.splice(newItems.findIndex((item) => item[0] == fromVertex && item[1] == toVertex), 1); - setItems(newItems); - onChange?.(newItems); - } + const itemRemoved = (fromVertex: string, toVertex: string) => { + const newItems = [...items]; + newItems.splice( + newItems.findIndex( + (item) => item[0] == fromVertex && item[1] == toVertex, + ), + 1, + ); + setItems(newItems); + onChange?.(newItems); + }; - const fromRef = React.createRef(); - const toRef = React.createRef(); + const fromRef = React.createRef(); + const toRef = React.createRef(); - return ( - // JSX markup goes here -
-

{title}

-
- - - - -
-
- { - items.map((item, index) => ( - itemRemoved?.(fromVertex, toVertex)} - fromVertex={item[0]} - toVertex={item[1]} - key={index}> - - )) - } -
-
- ); + return ( + // JSX markup goes here +
+

{title}

+
+ + + + +
+
+ {items.map((item, index) => ( + + itemRemoved?.(fromVertex, toVertex) + } + fromVertex={item[0]} + toVertex={item[1]} + key={index} + > + ))} +
+
+ ); }; export default EdgeCollector; diff --git a/webpage/app/edgeDisplay.tsx b/webpage/app/edgeDisplay.tsx index 3dcec8b4..0bb74567 100644 --- a/webpage/app/edgeDisplay.tsx +++ b/webpage/app/edgeDisplay.tsx @@ -1,20 +1,26 @@ - -import React from 'react'; +import React from "react"; interface EdgeDisplayProps { - fromVertex: string; - toVertex: string; - onClick?: (fromVertex: string, toVertex: string) => void; + fromVertex: string; + toVertex: string; + onClick?: (fromVertex: string, toVertex: string) => void; } -const EdgeDisplay: React.FC = ({ fromVertex, toVertex, onClick }) => { - return ( -
onClick?.(fromVertex, toVertex)} className={`text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`}> - {fromVertex} - - {toVertex} -
- ); +const EdgeDisplay: React.FC = ({ + fromVertex, + toVertex, + onClick, +}) => { + return ( +
onClick?.(fromVertex, toVertex)} + className={`text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`} + > + {fromVertex} + + {toVertex} +
+ ); }; export default EdgeDisplay; diff --git a/webpage/app/graphView.tsx b/webpage/app/graphView.tsx index b088f1f1..a51d9ebe 100644 --- a/webpage/app/graphView.tsx +++ b/webpage/app/graphView.tsx @@ -1,127 +1,148 @@ -import React, { useState, useRef, useEffect } from 'react'; -import styles from './style.module.css' -import Script from 'next/script'; -import Graph from 'react-graph-vis'; +import React, { useState, useRef, useEffect } from "react"; +import styles from "./style.module.css"; +import Script from "next/script"; +import Graph from "react-graph-vis"; const stringToAdjacencyMatrix = (str: string): number[][] | undefined => { - const lines = str.trim().split('\n'); - const matrix: number[][] = []; - for(const line of lines) { - const row: number[] = []; - for(const char of line.split(" ")) { - const result = parseFloat(char); - if(isNaN(result)) - return undefined; - row.push(result); - } - matrix.push(row); + const lines = str.trim().split("\n"); + const matrix: number[][] = []; + for (const line of lines) { + const row: number[] = []; + for (const char of line.split(" ")) { + const result = parseFloat(char); + if (isNaN(result)) return undefined; + row.push(result); } - return matrix; -} + matrix.push(row); + } + return matrix; +}; const adjacencyMatrixToString = (matrix: number[][]): string => { - let str = ""; - for(const row of matrix) { - for(const cell of row) { - str += cell + " "; - } - str = str.trimEnd(); - str += "\n"; + let str = ""; + for (const row of matrix) { + for (const cell of row) { + str += cell + " "; } - return str; -} + str = str.trimEnd(); + str += "\n"; + } + return str; +}; const adjacencyMatrixToDict = (matrix: number[][]): { [key: string]: any } => { - const dict: { [key: string]: any } = {}; - dict["nodes"] = []; - dict["edges"] = []; - for(let i = 0; i < matrix.length; i++) { - dict["nodes"].push({"id": i + 1, "label": "" + (i + 1)}); - } - for(let i = 0; i < matrix.length; i++) { - for(let j = 0; j < matrix[i].length; j++) { - if(matrix[i][j] > 0) - dict["edges"].push({"from": i + 1, "to": j + 1, "label": ""+matrix[i][j], "id": `${i + 1}-to-${j + 1}`}); - } + const dict: { [key: string]: any } = {}; + dict["nodes"] = []; + dict["edges"] = []; + for (let i = 0; i < matrix.length; i++) { + dict["nodes"].push({ id: i + 1, label: "" + (i + 1) }); + } + for (let i = 0; i < matrix.length; i++) { + for (let j = 0; j < matrix[i].length; j++) { + if (matrix[i][j] > 0) + dict["edges"].push({ + from: i + 1, + to: j + 1, + label: "" + matrix[i][j], + id: `${i + 1}-to-${j + 1}`, + }); } - return dict; -} + } + return dict; +}; interface GraphViewProps { - upload: boolean, - updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void, - initialAdjacencyMatrix?: number[][] + upload: boolean; + updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void; + initialAdjacencyMatrix?: number[][]; } +const GraphView: React.FC = ({ + upload, + updateAdjacencyMatrix, + initialAdjacencyMatrix, +}) => { + const [adjacencyMatrix, setAdjacencyMatrix] = useState( + initialAdjacencyMatrix ?? [], + ); + const [uploadMode, setUploadMode] = useState(false); + const textareaRef = useRef(null); + const [isClient, setIsClient] = useState(false); -const GraphView: React.FC = ({ upload, updateAdjacencyMatrix, initialAdjacencyMatrix }) => { - const [adjacencyMatrix, setAdjacencyMatrix] = useState(initialAdjacencyMatrix ?? []); - const [uploadMode, setUploadMode] = useState(false); - const textareaRef = useRef(null); - const [isClient, setIsClient] = useState(false); + useEffect(() => { + setIsClient(true); + }, []); - useEffect(() => { - setIsClient(true) - }, []); + if (upload && !uploadMode) { + setUploadMode(true); + } - if(upload && !uploadMode) { - setUploadMode(true); + const doOk = () => { + const textareaValue = textareaRef.current?.value; + const adj = stringToAdjacencyMatrix(textareaValue ?? ""); + if (adj !== undefined) { + setAdjacencyMatrix(adj); + updateAdjacencyMatrix(adj); } + setUploadMode(false); + }; - const doOk = () => { - const textareaValue = textareaRef.current?.value; - const adj = stringToAdjacencyMatrix(textareaValue ?? ""); - if(adj !== undefined) { - setAdjacencyMatrix(adj); - updateAdjacencyMatrix(adj); - } - setUploadMode(false); - } - - const doCancel = () => { - textareaRef.current!.value = adjacencyMatrixToString(adjacencyMatrix); - setUploadMode(false); - } + const doCancel = () => { + textareaRef.current!.value = adjacencyMatrixToString(adjacencyMatrix); + setUploadMode(false); + }; - const graph = adjacencyMatrixToDict(adjacencyMatrix); - const options = { - layout: { - hierarchical: false, - }, - nodes: { - color: { - background: "white", - border: "black" - }, - }, - edges: { - arrows: { - to: { enabled: true, scaleFactor: 1, type: 'arrow', }, - }, - smooth: true, - font: { - align: "top" - } - }, - }; + const graph = adjacencyMatrixToDict(adjacencyMatrix); + const options = { + layout: { + hierarchical: false, + }, + nodes: { + color: { + background: "white", + border: "black", + }, + }, + edges: { + arrows: { + to: { enabled: true, scaleFactor: 1, type: "arrow" }, + }, + smooth: true, + font: { + align: "top", + }, + }, + }; - return ( -
- { - uploadMode || !isClient ? ( -
- -
- - -
-
- ) : ( - - ) - } + return ( +
+ {uploadMode || !isClient ? ( +
+ +
+ + +
- ) + ) : ( + + )} +
+ ); }; export default GraphView; diff --git a/webpage/app/infoScreen.tsx b/webpage/app/infoScreen.tsx index b15a4986..1d978edf 100644 --- a/webpage/app/infoScreen.tsx +++ b/webpage/app/infoScreen.tsx @@ -1,15 +1,37 @@ -import styles from './style.module.css' +import styles from "./style.module.css"; const InfoScreen: React.FC = () => { - return ( -
-

MQT QUBOMaker: Pathfinder

-

This web app is a supporting GUI for the MQT QUBOMaker framework. It allows users to define pathfinding problems using a set of constraints that can be converted into a QUBO formulation by the framework.

-

Further details are given in the following paper: D. Rovara, N. Quetschlich, and R. Wille "A Framework to Formulate Pathfinding Problems for Quantum Computing", arXiv, 2024

-

The corresponding code can be found in the framework's GitHub repository.

-

MQT QUBOMaker is part of the Munich Quantum Toolkit (MQT) developed by the Chair for Design Automation at the Technical University of Munich.

-
- ); -} + return ( +
+

MQT QUBOMaker: Pathfinder

+

+ This web app is a supporting GUI for the MQT QUBOMaker framework. It + allows users to define pathfinding problems using a set of constraints + that can be converted into a QUBO formulation by the framework. +

+

+ Further details are given in the following paper: D. Rovara, N. + Quetschlich, and R. Wille{" "} + + "A Framework to Formulate Pathfinding Problems for Quantum + Computing" + + , arXiv, 2024 +

+

+ The corresponding code can be found in the framework's{" "} + GitHub repository + . +

+

+ MQT QUBOMaker is part of the{" "} + Munich Quantum Toolkit (MQT) + developed by the{" "} + Chair for Design Automation at + the Technical University of Munich. +

+
+ ); +}; export default InfoScreen; diff --git a/webpage/app/layout.tsx b/webpage/app/layout.tsx index 33965c3a..f6c23b85 100644 --- a/webpage/app/layout.tsx +++ b/webpage/app/layout.tsx @@ -1,23 +1,23 @@ -import type { Metadata } from 'next' -import { Inter } from 'next/font/google' -import './globals.css' -import Head from 'next/head' +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; +import Head from "next/head"; -const inter = Inter({ subsets: ['latin'] }) +const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { - title: 'MQT QUBOMaker: Pathfinder', - description: 'The UI assistant for MQT QUBOMaker', -} + title: "MQT QUBOMaker: Pathfinder", + description: "The UI assistant for MQT QUBOMaker", +}; export default function RootLayout({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) { return ( {children} - ) + ); } diff --git a/webpage/app/legal/page.tsx b/webpage/app/legal/page.tsx index 0ff50961..a6db4c76 100644 --- a/webpage/app/legal/page.tsx +++ b/webpage/app/legal/page.tsx @@ -1,90 +1,176 @@ -'use client' +"use client"; -import Image from 'next/image' +import Image from "next/image"; export default function Legal() { - return ( -
- -
-
-
Herausgeber
-
Technische Universität München
-
Arcisstraße 21
-
80333 München
-
Telefon: +49 89 289-01
-
poststelle@tum.de
-
- -
-
Rechtsform und Vertretung
-
Die Technische Universität München ist eine Körperschaft des Öffentlichen Rechts und staatliche Einrichtung (Art. 11 Abs. 1 BayHSchG). Sie wird gesetzlich vertreten durch den Präsidenten Prof. Dr. Thomas F. Hofmann.
-
+ return ( +
+ +
+
+
+ Herausgeber +
+
Technische Universität München
+
Arcisstraße 21
+
80333 München
+
Telefon: +49 89 289-01
+
poststelle@tum.de
+
-
-
Zuständige Aufsichtsbehörde
-
Bayerisches Staatsministerium für Wissenschaft und Kunst
-
+
+
+ Rechtsform und Vertretung +
+
+ Die Technische Universität München ist eine Körperschaft des + Öffentlichen Rechts und staatliche Einrichtung (Art. 11 Abs. 1 + BayHSchG). Sie wird gesetzlich vertreten durch den Präsidenten Prof. + Dr. Thomas F. Hofmann. +
+
-
-
Umsatzsteueridentifikationnummer
-
DE811193231 (gemäß § 27a Umsatzsteuergesetz)
-
+
+
+ Zuständige Aufsichtsbehörde +
+
Bayerisches Staatsministerium für Wissenschaft und Kunst
+
-
-
Inhaltlich verantwortlich
-
Prof. Dr. Robert Wille
-
Arcisstr. 21
-
80333 München
-
E-Mail: robert.wille(at)tum.de
-
+
+
+ Umsatzsteueridentifikationnummer +
+
DE811193231 (gemäß § 27a Umsatzsteuergesetz)
+
-
- Namentlich gekennzeichnete Internetseiten geben die Auffassungen und Erkenntnisse der genannten Personen wieder. -
+
+
+ Inhaltlich verantwortlich +
+
Prof. Dr. Robert Wille
+
Arcisstr. 21
+
80333 München
+
E-Mail: robert.wille(at)tum.de
+
-
-
Nutzungsbedingungen
-
Texte, Bilder, Grafiken sowie die Gestaltung dieses Webauftritts können dem Urheberrecht unterliegen. Nicht urheberrechtlich geschützt sind nach § 5 des Urheberrechtsgesetz (UrhG)
-
    -
  • - Gesetze, Verordnungen, amtliche Erlasse und Bekanntmachungen sowie Entscheidungen und amtlich verfasste Leitsätze zu Entscheidungen und
  • -
  • - andere amtliche Werke, die im amtlichen Interesse zur allgemeinen Kenntnisnahme veröffentlicht worden sind, mit der Einschränkung, dass die Bestimmungen über Änderungsverbot und Quellenangabe in § 62 Abs. 1 bis 3 und § 63 Abs. 1 und 2 UrhG entsprechend anzuwenden sind.
  • -
-
+
+ Namentlich gekennzeichnete Internetseiten geben die Auffassungen und + Erkenntnisse der genannten Personen wieder. +
-
- Als Privatperson dürfen Sie urheberrechtlich geschütztes Material zum privaten und sonstigen eigenen Gebrauch im Rahmen des § 53 UrhG verwenden. Eine Vervielfältigung oder Verwendung urheberrechtlich geschützten Materials dieser Seiten oder Teilen davon in anderen elektronischen oder gedruckten Publikationen und deren Veröffentlichung ist nur mit unserer Einwilligung gestattet. Diese Einwilligung erteilen auf Anfrage die für den Inhalt Verantwortlichen. Der Nachdruck und die Auswertung von Pressemitteilungen und Reden sind mit Quellenangabe allgemein gestattet. -
+
+
+ Nutzungsbedingungen +
+
+ Texte, Bilder, Grafiken sowie die Gestaltung dieses Webauftritts + können dem Urheberrecht unterliegen. Nicht urheberrechtlich + geschützt sind nach § 5 des Urheberrechtsgesetz (UrhG) +
+
    +
  • + - Gesetze, Verordnungen, amtliche Erlasse und Bekanntmachungen + sowie Entscheidungen und amtlich verfasste Leitsätze zu + Entscheidungen und +
  • +
  • + - andere amtliche Werke, die im amtlichen Interesse zur + allgemeinen Kenntnisnahme veröffentlicht worden sind, mit der + Einschränkung, dass die Bestimmungen über Änderungsverbot und + Quellenangabe in § 62 Abs. 1 bis 3 und § 63 Abs. 1 und 2 UrhG + entsprechend anzuwenden sind. +
  • +
+
-
- Weiterhin können Texte, Bilder, Grafiken und sonstige Dateien ganz oder teilweise dem Urheberrecht Dritter unterliegen. Auch über das Bestehen möglicher Rechte Dritter geben Ihnen die für den Inhalt Verantwortlichen nähere Auskünfte. -
+
+ Als Privatperson dürfen Sie urheberrechtlich geschütztes Material zum + privaten und sonstigen eigenen Gebrauch im Rahmen des § 53 UrhG + verwenden. Eine Vervielfältigung oder Verwendung urheberrechtlich + geschützten Materials dieser Seiten oder Teilen davon in anderen + elektronischen oder gedruckten Publikationen und deren + Veröffentlichung ist nur mit unserer Einwilligung gestattet. Diese + Einwilligung erteilen auf Anfrage die für den Inhalt Verantwortlichen. + Der Nachdruck und die Auswertung von Pressemitteilungen und Reden sind + mit Quellenangabe allgemein gestattet. +
-
-
Haftungsausschluss
-
- Alle in diesem Webauftritt bereitgestellten Informationen haben wir nach bestem Wissen und Gewissen erarbeitet und geprüft. Eine Gewähr für die jederzeitige Aktualität, Richtigkeit, Vollständigkeit und Verfügbarkeit der bereit gestellten Informationen können wir allerdings nicht übernehmen. In Vertragsverhältnis mit den Nutzern des Webauftritts kommt nicht zustande. -
-
+
+ Weiterhin können Texte, Bilder, Grafiken und sonstige Dateien ganz + oder teilweise dem Urheberrecht Dritter unterliegen. Auch über das + Bestehen möglicher Rechte Dritter geben Ihnen die für den Inhalt + Verantwortlichen nähere Auskünfte. +
-
- Wir haften nicht für Schäden, die durch die Nutzung dieses Webauftritts entstehen. Dieser Haftungsausschluss gilt nicht, soweit die Vorschriften des § 839 BGB (Haftung bei Amtspflichtverletzung) einschlägig sind. Für etwaige Schäden, die beim Aufrufen oder Herunterladen von Daten durch Schadsoftware oder der Installation oder Nutzung von Software verursacht werden, übernehmen wir keine Haftung. -
+
+
+ Haftungsausschluss +
+
+ Alle in diesem Webauftritt bereitgestellten Informationen haben wir + nach bestem Wissen und Gewissen erarbeitet und geprüft. Eine Gewähr + für die jederzeitige Aktualität, Richtigkeit, Vollständigkeit und + Verfügbarkeit der bereit gestellten Informationen können wir + allerdings nicht übernehmen. In Vertragsverhältnis mit den Nutzern + des Webauftritts kommt nicht zustande. +
+
-
-
Links
-
- Von unseren eigenen Inhalten sind Querverweise ("Links") auf die Webseiten anderer Anbieter zu unterscheiden. Durch diese Links ermöglichen wir lediglich den Zugang zur Nutzung fremder Inhalte nach § 8 Telemediengesetz. Bei der erstmaligen Verknüpfung mit diesen Internetangeboten haben wir diese fremden Inhalte daraufhin überprüft, ob durch sie eine mögliche zivilrechtliche oder strafrechtliche Verantwortlichkeit ausgelöst wird. Wir können diese fremden Inhalte aber nicht ständig auf Veränderungen überprüfen und daher auch keine Verantwortung dafür übernehmen. Für illegale, fehlerhafte oder unvollständige Inhalte und insbesondere für Schäden, die aus der Nutzung oder Nichtnutzung von Informationen Dritter entstehen, haftet allein der jeweilige Anbieter der Seite. -
-
+
+ Wir haften nicht für Schäden, die durch die Nutzung dieses + Webauftritts entstehen. Dieser Haftungsausschluss gilt nicht, soweit + die Vorschriften des § 839 BGB (Haftung bei Amtspflichtverletzung) + einschlägig sind. Für etwaige Schäden, die beim Aufrufen oder + Herunterladen von Daten durch Schadsoftware oder der Installation oder + Nutzung von Software verursacht werden, übernehmen wir keine Haftung. +
-


+
+
+ Links +
+
+ Von unseren eigenen Inhalten sind Querverweise ("Links") + auf die Webseiten anderer Anbieter zu unterscheiden. Durch diese + Links ermöglichen wir lediglich den Zugang zur Nutzung fremder + Inhalte nach § 8 Telemediengesetz. Bei der erstmaligen Verknüpfung + mit diesen Internetangeboten haben wir diese fremden Inhalte + daraufhin überprüft, ob durch sie eine mögliche zivilrechtliche oder + strafrechtliche Verantwortlichkeit ausgelöst wird. Wir können diese + fremden Inhalte aber nicht ständig auf Veränderungen überprüfen und + daher auch keine Verantwortung dafür übernehmen. Für illegale, + fehlerhafte oder unvollständige Inhalte und insbesondere für + Schäden, die aus der Nutzung oder Nichtnutzung von Informationen + Dritter entstehen, haftet allein der jeweilige Anbieter der Seite.
-
- ); +
+ +
+

+
+
+ + ); } diff --git a/webpage/app/page.tsx b/webpage/app/page.tsx index 05fc1f16..69a44c45 100644 --- a/webpage/app/page.tsx +++ b/webpage/app/page.tsx @@ -1,27 +1,30 @@ -'use client' +"use client"; -import Image from 'next/image' -import Toggle from './toggle' -import ToggleBag from './toggleBag' -import EdgeDisplay from './edgeDisplay' -import EdgeCollector from './edgeCollector' -import GraphView from './graphView' -import { useEffect, useRef, useState } from 'react' -import TitledTextbox from './titledTextbox' -import Settings from './settings' -import PathPosIsCollector from './pathPosIsCollector' -import InfoScreen from './infoScreen' +import Image from "next/image"; +import Toggle from "./toggle"; +import ToggleBag from "./toggleBag"; +import EdgeDisplay from "./edgeDisplay"; +import EdgeCollector from "./edgeCollector"; +import GraphView from "./graphView"; +import { useEffect, useRef, useState } from "react"; +import TitledTextbox from "./titledTextbox"; +import Settings from "./settings"; +import PathPosIsCollector from "./pathPosIsCollector"; +import InfoScreen from "./infoScreen"; function download(filename: string, text: string) { - if(text === "") { + if (text === "") { alert("Please select an encoding!"); return; } - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); - element.setAttribute('download', filename); + var element = document.createElement("a"); + element.setAttribute( + "href", + "data:text/plain;charset=utf-8," + encodeURIComponent(text), + ); + element.setAttribute("download", filename); - element.style.display = 'none'; + element.style.display = "none"; document.body.appendChild(element); element.click(); @@ -30,94 +33,233 @@ function download(filename: string, text: string) { } export default function Home() { - const [settings, setSettings] = useState(new Settings()) + const [settings, setSettings] = useState(new Settings()); const [doUpload, setDoUpload] = useState(false); - const [adjacencyMatrix, setAdjacencyMatrix] = useState([[0, 4, 2, 0], + const [adjacencyMatrix, setAdjacencyMatrix] = useState([ + [0, 4, 2, 0], [3, 0, 0, 2], [2, 0, 0, 5], - [2, 0, 5, 0] - ]); - const updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void = (adjacencyMatrix) => { + [2, 0, 5, 0], + ]); + const updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void = ( + adjacencyMatrix, + ) => { setAdjacencyMatrix(adjacencyMatrix); setSettings(settings.setMaxPathLength(adjacencyMatrix.length)); - } + }; useEffect(() => { - if(doUpload) - setDoUpload(false); + if (doUpload) setDoUpload(false); }, [doUpload]); useEffect(() => { - setVertices(Array.from(Array(adjacencyMatrix.length).keys()).map(i => (i + 1).toFixed(0))); - - }, [adjacencyMatrix]) + setVertices( + Array.from(Array(adjacencyMatrix.length).keys()).map((i) => + (i + 1).toFixed(0), + ), + ); + }, [adjacencyMatrix]); - const [vertices, setVertices] = useState(Array.from(Array(adjacencyMatrix.length).keys()).map(i => (i + 1).toFixed(0))); + const [vertices, setVertices] = useState( + Array.from(Array(adjacencyMatrix.length).keys()).map((i) => + (i + 1).toFixed(0), + ), + ); const [showInfo, setShowInfo] = useState(false); return (
- { - showInfo ? : - } + {showInfo ? ( + + ) : ( + + )}
- - - + + +
- setSettings(settings.setEncoding(states))}> + setSettings(settings.setEncoding(states))} + >
- { - const newNum = parseInt(n); - if(!isNaN(newNum) && newNum > 0) - setSettings(settings.setNPaths(newNum)); - }}> - { - const newNum = parseInt(n); - if(!isNaN(newNum) && newNum > 0) - setSettings(settings.setMaxPathLength(newNum)); - }}> + { + const newNum = parseInt(n); + if (!isNaN(newNum) && newNum > 0) + setSettings(settings.setNPaths(newNum)); + }} + > + { + const newNum = parseInt(n); + if (!isNaN(newNum) && newNum > 0) + setSettings(settings.setMaxPathLength(newNum)); + }} + >
- setSettings(settings.setGeneralSettings(states))}> - setSettings(settings.setExactlyOnceVertices(states))} all={true} title='The following vertices must appear exactly once in each path' items={vertices}> - setSettings(settings.setAtLeastOnceVertices(states))} all={true} title='The following vertices must appear at least once in each path' items={vertices}> - setSettings(settings.setAtMostOnceVertices(states))} all={true} title='The following vertices must appear at most once in each path' items={vertices}> + + setSettings(settings.setGeneralSettings(states)) + } + > + + setSettings(settings.setExactlyOnceVertices(states)) + } + all={true} + title="The following vertices must appear exactly once in each path" + items={vertices} + > + + setSettings(settings.setAtLeastOnceVertices(states)) + } + all={true} + title="The following vertices must appear at least once in each path" + items={vertices} + > + + setSettings(settings.setAtMostOnceVertices(states)) + } + all={true} + title="The following vertices must appear at most once in each path" + items={vertices} + > - setSettings(settings.setShareNoVertices(states))} all={true} title='The following paths may not share vertices' items={Array(settings.nPaths).fill(0).map((_, i) => (i + 1) + "")}> - setSettings(settings.setShareNoVertices(states))} all={true} title='The following paths may not share edges' items={Array(settings.nPaths).fill(0).map((_, i) => (i + 1) + "")}> + + setSettings(settings.setShareNoVertices(states)) + } + all={true} + title="The following paths may not share vertices" + items={Array(settings.nPaths) + .fill(0) + .map((_, i) => i + 1 + "")} + > + + setSettings(settings.setShareNoVertices(states)) + } + all={true} + title="The following paths may not share edges" + items={Array(settings.nPaths) + .fill(0) + .map((_, i) => i + 1 + "")} + >
- setSettings(settings.setPathPositionIs(entries))} title="Vertex positions" nPaths={settings.nPaths} pathLength={settings.maxPathLength} allVertices={vertices}> - setSettings(settings.setExactlyOnceEdges(edges))} title="The following edges must appear exactly once in each path" allVertices={vertices}> - setSettings(settings.setAtLeastOnceEdges(edges))} title="The following edges must appear at least once in each path" allVertices={vertices}> - setSettings(settings.setAtMostOnceEdges(edges))} title="The following edges must appear at most once in each path" allVertices={vertices}> - + + setSettings(settings.setPathPositionIs(entries)) + } + title="Vertex positions" + nPaths={settings.nPaths} + pathLength={settings.maxPathLength} + allVertices={vertices} + > + + setSettings(settings.setExactlyOnceEdges(edges)) + } + title="The following edges must appear exactly once in each path" + allVertices={vertices} + > + + setSettings(settings.setAtLeastOnceEdges(edges)) + } + title="The following edges must appear at least once in each path" + allVertices={vertices} + > + + setSettings(settings.setAtMostOnceEdges(edges)) + } + title="The following edges must appear at most once in each path" + allVertices={vertices} + > - setSettings(settings.setPrecedence(edges))} title="The following precedence constraints must be fulfilled" allVertices={vertices}> + setSettings(settings.setPrecedence(edges))} + title="The following precedence constraints must be fulfilled" + allVertices={vertices} + >
- ) + ); } diff --git a/webpage/app/pathPosIs.tsx b/webpage/app/pathPosIs.tsx index 65902e61..af5f6e58 100644 --- a/webpage/app/pathPosIs.tsx +++ b/webpage/app/pathPosIs.tsx @@ -1,24 +1,31 @@ - -import React from 'react'; +import React from "react"; interface PathPosIsProps { - path: number; - position: number; - vertex: string; - onClick?: (path: number, position: number, vertex: string) => void; + path: number; + position: number; + vertex: string; + onClick?: (path: number, position: number, vertex: string) => void; } -const PathPosIs: React.FC = ({ path, position, vertex, onClick }) => { - return ( -
onClick?.(path, position, vertex)} className={`justify-around inline-flex flex-wrap-nowrap text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`}> - Path - {path} - position - {position} - is: - {vertex} -
- ); +const PathPosIs: React.FC = ({ + path, + position, + vertex, + onClick, +}) => { + return ( +
onClick?.(path, position, vertex)} + className={`justify-around inline-flex flex-wrap-nowrap text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`} + > + Path + {path} + position + {position} + is: + {vertex} +
+ ); }; export default PathPosIs; diff --git a/webpage/app/pathPosIsCollector.tsx b/webpage/app/pathPosIsCollector.tsx index d61e1128..d5bc236d 100644 --- a/webpage/app/pathPosIsCollector.tsx +++ b/webpage/app/pathPosIsCollector.tsx @@ -1,89 +1,108 @@ -import React, { useState } from 'react'; -import Toggle from './toggle'; -import PathPosIs from './pathPosIs'; +import React, { useState } from "react"; +import Toggle from "./toggle"; +import PathPosIs from "./pathPosIs"; interface PathPosIsCollectorProps { - title: string; - cols?: number; - allVertices: string[]; - nPaths: number; - pathLength: number; - onChange?: (items: string[][]) => void; + title: string; + cols?: number; + allVertices: string[]; + nPaths: number; + pathLength: number; + onChange?: (items: string[][]) => void; } -const PathPosIsCollector: React.FC = ({ title, cols = 2, allVertices, nPaths, pathLength, onChange}) => { - // Component logic goes here - const [items, setItems] = useState([]); +const PathPosIsCollector: React.FC = ({ + title, + cols = 2, + allVertices, + nPaths, + pathLength, + onChange, +}) => { + // Component logic goes here + const [items, setItems] = useState([]); - const itemAdded = (path: string, position: string, vertex: string) => { - if(vertex === "") - return; - if(items.some((item) => item[0] == path && item[1] == position && item[2] == vertex)) - return; - const newItems = [...items]; - newItems.push([path, position, vertex]); - setItems(newItems); - onChange?.(newItems); - } + const itemAdded = (path: string, position: string, vertex: string) => { + if (vertex === "") return; + if ( + items.some( + (item) => item[0] == path && item[1] == position && item[2] == vertex, + ) + ) + return; + const newItems = [...items]; + newItems.push([path, position, vertex]); + setItems(newItems); + onChange?.(newItems); + }; - const itemRemoved = (path: string, position: string, vertex: string) => { - const newItems = [...items]; - newItems.splice(newItems.findIndex((item) => item[0] == path && item[1] == position && item[2] == vertex), 1); - setItems(newItems); - onChange?.(newItems); - } + const itemRemoved = (path: string, position: string, vertex: string) => { + const newItems = [...items]; + newItems.splice( + newItems.findIndex( + (item) => item[0] == path && item[1] == position && item[2] == vertex, + ), + 1, + ); + setItems(newItems); + onChange?.(newItems); + }; - const pathRef = React.createRef(); - const posRef = React.createRef(); - const vertexRef = React.createRef(); + const pathRef = React.createRef(); + const posRef = React.createRef(); + const vertexRef = React.createRef(); - return ( - // JSX markup goes here -
-

{title}

-
- Path - - position - - is - - -
-
- { - items.map((item, index) => ( - itemRemoved?.(path + "", position + "", vertex)} - path={parseInt(item[0])} - position={parseInt(item[1])} - vertex={item[2]} - key={index}> - - )) - } -
-
- ); + return ( + // JSX markup goes here +
+

{title}

+
+ Path + + position + + is + + +
+
+ {items.map((item, index) => ( + + itemRemoved?.(path + "", position + "", vertex) + } + path={parseInt(item[0])} + position={parseInt(item[1])} + vertex={item[2]} + key={index} + > + ))} +
+
+ ); }; export default PathPosIsCollector; diff --git a/webpage/app/settings.ts b/webpage/app/settings.ts index 348e0130..cd0ee50c 100644 --- a/webpage/app/settings.ts +++ b/webpage/app/settings.ts @@ -9,274 +9,321 @@ */ class Settings { + encoding: number = -1; + nPaths: number = 1; + maxPathLength: number = 4; - encoding: number = -1; - nPaths: number = 1; - maxPathLength: number = 4; + loop: boolean = false; + minimizeWeight: boolean = false; + maximizeWeight: boolean = false; - loop: boolean = false; - minimizeWeight: boolean = false; - maximizeWeight: boolean = false; + exactlyOnceVertices: number[] = []; + atLeastOnceVertices: number[] = []; + atMostOnceVertices: number[] = []; - exactlyOnceVertices: number[] = []; - atLeastOnceVertices: number[] = []; - atMostOnceVertices: number[] = []; + exactlyOnceEdges: string[][] = []; + atLeastOnceEdges: string[][] = []; + atMostOnceEdges: string[][] = []; - exactlyOnceEdges: string[][] = []; - atLeastOnceEdges: string[][] = []; - atMostOnceEdges: string[][] = []; + pathPositionIs: string[][] = []; - pathPositionIs: string[][] = []; + precedences: string[][] = []; - precedences: string[][] = []; + shareNoEdges: number[] = []; + shareNoVertices: number[] = []; - shareNoEdges: number[] = []; - shareNoVertices: number[] = []; + setEncoding(encoding: boolean[]) { + const clone = this.clone(); + clone.encoding = encoding.indexOf(true); + return clone; + } - setEncoding(encoding: boolean[]) { - const clone = this.clone(); - clone.encoding = encoding.indexOf(true); - return clone; - } + setNPaths(nPaths: number) { + const clone = this.clone(); + clone.nPaths = nPaths; + return clone; + } - setNPaths(nPaths: number) { - const clone = this.clone(); - clone.nPaths = nPaths; - return clone; - } + setMaxPathLength(maxPathLength: number) { + const clone = this.clone(); + clone.maxPathLength = maxPathLength; + return clone; + } - setMaxPathLength(maxPathLength: number) { - const clone = this.clone(); - clone.maxPathLength = maxPathLength; - return clone; - } + setGeneralSettings(settings: boolean[]) { + const clone = this.clone(); + clone.loop = settings[0]; + clone.minimizeWeight = settings[1]; + clone.maximizeWeight = settings[2]; + return clone; + } - setGeneralSettings(settings: boolean[]) { - const clone = this.clone(); - clone.loop = settings[0]; - clone.minimizeWeight = settings[1]; - clone.maximizeWeight = settings[2]; - return clone; - } + setExactlyOnceVertices(exactlyOnceVertices: boolean[]) { + const clone = this.clone(); + clone.exactlyOnceVertices = exactlyOnceVertices + .slice(1) + .map((value, index) => (value ? index : -1)) + .filter((value) => value !== -1); + return clone; + } - setExactlyOnceVertices(exactlyOnceVertices: boolean[]) { - const clone = this.clone(); - clone.exactlyOnceVertices = exactlyOnceVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); - return clone; - } + setAtLeastOnceVertices(atLeastOnceVertices: boolean[]) { + const clone = this.clone(); + clone.atLeastOnceVertices = atLeastOnceVertices + .slice(1) + .map((value, index) => (value ? index : -1)) + .filter((value) => value !== -1); + return clone; + } - setAtLeastOnceVertices(atLeastOnceVertices: boolean[]) { - const clone = this.clone(); - clone.atLeastOnceVertices = atLeastOnceVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); - return clone; - } + setAtMostOnceVertices(atMostOnceVertices: boolean[]) { + const clone = this.clone(); + clone.atMostOnceVertices = atMostOnceVertices + .slice(1) + .map((value, index) => (value ? index : -1)) + .filter((value) => value !== -1); + return clone; + } - setAtMostOnceVertices(atMostOnceVertices: boolean[]) { - const clone = this.clone(); - clone.atMostOnceVertices = atMostOnceVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); - return clone; - } + setShareNoVertices(shareNoVertices: boolean[]) { + const clone = this.clone(); + clone.shareNoVertices = shareNoVertices + .slice(1) + .map((value, index) => (value ? index : -1)) + .filter((value) => value !== -1); + return clone; + } - setShareNoVertices(shareNoVertices: boolean[]) { - const clone = this.clone(); - clone.shareNoVertices = shareNoVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); - return clone; - } + setShareNoEdges(shareNoEdges: boolean[]) { + const clone = this.clone(); + clone.shareNoEdges = shareNoEdges + .slice(1) + .map((value, index) => (value ? index : -1)) + .filter((value) => value !== -1); + return clone; + } - setShareNoEdges(shareNoEdges: boolean[]) { - const clone = this.clone(); - clone.shareNoEdges = shareNoEdges.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); - return clone; - } + setPathPositionIs(pathPositionIs: string[][]) { + const clone = this.clone(); + clone.pathPositionIs = pathPositionIs; + return clone; + } - setPathPositionIs(pathPositionIs: string[][]) { - const clone = this.clone(); - clone.pathPositionIs = pathPositionIs; - return clone; - } + setExactlyOnceEdges(exactlyOnceEdges: string[][]) { + const clone = this.clone(); + clone.exactlyOnceEdges = exactlyOnceEdges; + return clone; + } - setExactlyOnceEdges(exactlyOnceEdges: string[][]) { - const clone = this.clone(); - clone.exactlyOnceEdges = exactlyOnceEdges; - return clone; - } + setAtLeastOnceEdges(atLeastOnceEdges: string[][]) { + const clone = this.clone(); + clone.atLeastOnceEdges = atLeastOnceEdges; + return clone; + } - setAtLeastOnceEdges(atLeastOnceEdges: string[][]) { - const clone = this.clone(); - clone.atLeastOnceEdges = atLeastOnceEdges; - return clone; - } + setAtMostOnceEdges(atMostOnceEdges: string[][]) { + const clone = this.clone(); + clone.atMostOnceEdges = atMostOnceEdges; + return clone; + } - setAtMostOnceEdges(atMostOnceEdges: string[][]) { - const clone = this.clone(); - clone.atMostOnceEdges = atMostOnceEdges; - return clone; - } + setPrecedence(precedences: string[][]) { + const clone = this.clone(); + clone.precedences = precedences; + return clone; + } - setPrecedence(precedences: string[][]) { - const clone = this.clone(); - clone.precedences = precedences; - return clone; - } + clone() { + let clone = new Settings(); + clone.encoding = this.encoding; + clone.nPaths = this.nPaths; + clone.maxPathLength = this.maxPathLength; + clone.loop = this.loop; + clone.minimizeWeight = this.minimizeWeight; + clone.maximizeWeight = this.maximizeWeight; + clone.exactlyOnceVertices = this.exactlyOnceVertices.slice(); + clone.atLeastOnceVertices = this.atLeastOnceVertices.slice(); + clone.atMostOnceVertices = this.atMostOnceVertices.slice(); + clone.exactlyOnceEdges = this.exactlyOnceEdges.map((value) => + value.slice(), + ); + clone.atLeastOnceEdges = this.atLeastOnceEdges.map((value) => + value.slice(), + ); + clone.atMostOnceEdges = this.atMostOnceEdges.map((value) => value.slice()); + clone.precedences = this.precedences.map((value) => value.slice()); + clone.shareNoEdges = this.shareNoEdges.slice(); + clone.shareNoVertices = this.shareNoVertices.slice(); + clone.pathPositionIs = this.pathPositionIs.map((value) => value.slice()); + return clone; + } - clone() { - let clone = new Settings(); - clone.encoding = this.encoding; - clone.nPaths = this.nPaths; - clone.maxPathLength = this.maxPathLength; - clone.loop = this.loop; - clone.minimizeWeight = this.minimizeWeight; - clone.maximizeWeight = this.maximizeWeight; - clone.exactlyOnceVertices = this.exactlyOnceVertices.slice(); - clone.atLeastOnceVertices = this.atLeastOnceVertices.slice(); - clone.atMostOnceVertices = this.atMostOnceVertices.slice(); - clone.exactlyOnceEdges = this.exactlyOnceEdges.map(value => value.slice()); - clone.atLeastOnceEdges = this.atLeastOnceEdges.map(value => value.slice()); - clone.atMostOnceEdges = this.atMostOnceEdges.map(value => value.slice()); - clone.precedences = this.precedences.map(value => value.slice()); - clone.shareNoEdges = this.shareNoEdges.slice(); - clone.shareNoVertices = this.shareNoVertices.slice(); - clone.pathPositionIs = this.pathPositionIs.map(value => value.slice()); - return clone; + encodingToString() { + if (this.encoding === -1) { + throw new Error("Encoding not set"); + } + if (this.encoding == 0) { + return "ONE_HOT"; } + if (this.encoding == 1) { + return "UNARY"; + } + if (this.encoding == 2) { + return "BINARY"; + } + } - encodingToString() { - if(this.encoding === -1) { - throw new Error("Encoding not set"); - } - if(this.encoding == 0) { - return "ONE_HOT"; - } - if(this.encoding == 1) { - return "UNARY"; - } - if(this.encoding == 2) { - return "BINARY"; - } + toJson() { + if (this.encoding === -1) { + throw new Error("Encoding not set"); + } + const object: { [key: string]: any } = {}; + object["settings"] = { + encoding: this.encodingToString(), + n_paths: this.nPaths, + max_path_length: this.maxPathLength, + loops: this.loop, + }; + if (this.minimizeWeight && this.maximizeWeight) { + throw new Error("Cannot minimize and maximize weight at the same time"); + } else if (this.minimizeWeight) { + object["objective_function"] = { + type: "MinimizePathLength", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + }; + } else if (this.maximizeWeight) { + object["objective_function"] = { + type: "MaximizePathLength", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + }; } - toJson() { - if(this.encoding === -1) { - throw new Error("Encoding not set"); - } - const object: { [key: string]: any} = {}; - object["settings"] = { - "encoding": this.encodingToString(), - "n_paths": this.nPaths, - "max_path_length": this.maxPathLength, - "loops": this.loop, - }; - if(this.minimizeWeight && this.maximizeWeight) { - throw new Error("Cannot minimize and maximize weight at the same time"); - } else if(this.minimizeWeight) { - object["objective_function"] = { - "type": "MinimizePathLength", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1) - }; - } else if(this.maximizeWeight) { - object["objective_function"] = { - "type": "MaximizePathLength", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1) - }; + const constraints: { [key: string]: any }[] = []; + constraints.push({ + type: "PathIsValid", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + }); + //--------------------- Vertices --------------------- + if (this.exactlyOnceVertices.length > 0) { + constraints.push({ + type: "PathContainsVerticesExactlyOnce", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + vertices: this.exactlyOnceVertices.map((value) => value + 1), + }); + } + if (this.atLeastOnceVertices.length > 0) { + constraints.push({ + type: "PathContainsVerticesAtLeastOnce", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + vertices: this.atLeastOnceVertices.map((value) => value + 1), + }); + } + if (this.atMostOnceVertices.length > 0) { + constraints.push({ + type: "PathContainsVerticesAtMostOnce", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + vertices: this.atMostOnceVertices.map((value) => value + 1), + }); + } + //--------------------- Edges --------------------- + if (this.exactlyOnceEdges.length > 0) { + constraints.push({ + type: "PathContainsEdgesExactlyOnce", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + edges: this.exactlyOnceEdges.map((pair) => + pair.map((val) => parseInt(val)), + ), + }); + } + if (this.atLeastOnceEdges.length > 0) { + constraints.push({ + type: "PathContainsEdgesAtLeastOnce", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + edges: this.atLeastOnceEdges.map((pair) => + pair.map((val) => parseInt(val)), + ), + }); + } + if (this.atMostOnceEdges.length > 0) { + constraints.push({ + type: "PathContainsEdgesAtMostOnce", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + edges: this.atMostOnceEdges.map((pair) => + pair.map((val) => parseInt(val)), + ), + }); + } + if (this.pathPositionIs.length > 0) { + const keyValue: [string[], string][] = this.pathPositionIs.map( + (value) => [[value[0], value[1]], value[2]], + ); + const map = new Map(); + for (const entry of keyValue) { + if (!map.has(JSON.stringify(entry[0]))) { + map.set(JSON.stringify(entry[0]), []); } + map.get(JSON.stringify(entry[0]))!.push(entry[1]); + } + console.log(map.keys()); - const constraints: { [key: string]: any}[] = []; + for (const entry of map.keys()) { + const entry_array = JSON.parse(entry); constraints.push({ - "type": "PathIsValid", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + type: "PathPositionIs", + position: parseInt(entry_array[1]), + vertices: map.get(entry)!.map((vertex) => parseInt(vertex)), + path_id: parseInt(entry_array[0]), }); - //--------------------- Vertices --------------------- - if(this.exactlyOnceVertices.length > 0) { - constraints.push({ - "type": "PathContainsVerticesExactlyOnce", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - "vertices": this.exactlyOnceVertices.map(value => value + 1) - }); - } - if(this.atLeastOnceVertices.length > 0) { - constraints.push({ - "type": "PathContainsVerticesAtLeastOnce", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - "vertices": this.atLeastOnceVertices.map(value => value + 1) - }); - } - if(this.atMostOnceVertices.length > 0) { - constraints.push({ - "type": "PathContainsVerticesAtMostOnce", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - "vertices": this.atMostOnceVertices.map(value => value + 1) - }); - } - //--------------------- Edges --------------------- - if(this.exactlyOnceEdges.length > 0) { - constraints.push({ - "type": "PathContainsEdgesExactlyOnce", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - "edges": this.exactlyOnceEdges.map(pair => pair.map((val) => parseInt(val))) - }); - } - if(this.atLeastOnceEdges.length > 0) { - constraints.push({ - "type": "PathContainsEdgesAtLeastOnce", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - "edges": this.atLeastOnceEdges.map(pair => pair.map((val) => parseInt(val))) - }); - } - if(this.atMostOnceEdges.length > 0) { - constraints.push({ - "type": "PathContainsEdgesAtMostOnce", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - "edges": this.atMostOnceEdges.map(pair => pair.map((val) => parseInt(val))) - }); - } - if(this.pathPositionIs.length > 0) { - const keyValue: [string[], string][] = this.pathPositionIs.map(value => [[value[0], value[1]], value[2]]); - const map = new Map(); - for(const entry of keyValue) { - if(!map.has(JSON.stringify(entry[0]))) { - map.set(JSON.stringify(entry[0]), []); - } - map.get(JSON.stringify(entry[0]))!.push(entry[1]); - } - console.log(map.keys()) - - for(const entry of map.keys()) { - const entry_array = JSON.parse(entry); - constraints.push({ - "type": "PathPositionIs", - "position": parseInt(entry_array[1]), - "vertices": map.get(entry)!.map(vertex => parseInt(vertex)), - "path_id": parseInt(entry_array[0]) - }); - - } - } - if(this.precedences.length > 0) { - constraints.push({ - "type": "PrecedenceConstraint", - "precedences": this.precedences.map(value => ({ - "before": parseInt(value[0]), - "after": parseInt(value[1]) - })) - }); - } - - if(this.shareNoEdges.length > 0) { - constraints.push({ - "type": "PathsShareNoEdges", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - }); - } - if(this.shareNoVertices.length > 0) { - constraints.push({ - "type": "PathsShareNoVertices", - "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), - }); - } + } + } + if (this.precedences.length > 0) { + constraints.push({ + type: "PrecedenceConstraint", + precedences: this.precedences.map((value) => ({ + before: parseInt(value[0]), + after: parseInt(value[1]), + })), + }); + } - object["constraints"] = constraints; - return JSON.stringify(object); + if (this.shareNoEdges.length > 0) { + constraints.push({ + type: "PathsShareNoEdges", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + }); } + if (this.shareNoVertices.length > 0) { + constraints.push({ + type: "PathsShareNoVertices", + path_ids: Array(this.nPaths) + .fill(0) + .map((_, index) => index + 1), + }); + } + + object["constraints"] = constraints; + return JSON.stringify(object); + } } -export default Settings +export default Settings; diff --git a/webpage/app/titledTextbox.tsx b/webpage/app/titledTextbox.tsx index f0e774d9..ceebac1e 100644 --- a/webpage/app/titledTextbox.tsx +++ b/webpage/app/titledTextbox.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from "react"; type TitledTextboxProps = { title: string; @@ -6,11 +6,20 @@ type TitledTextboxProps = { onChange?: (value: string) => void; }; -const TitledTextbox: React.FC = ({ title, defaultValue, onChange }) => { +const TitledTextbox: React.FC = ({ + title, + defaultValue, + onChange, +}) => { return (
- onChange?.(e.currentTarget.value!)} /> + onChange?.(e.currentTarget.value!)} + />
); }; diff --git a/webpage/app/toggle.tsx b/webpage/app/toggle.tsx index 05e49e87..4128de8b 100644 --- a/webpage/app/toggle.tsx +++ b/webpage/app/toggle.tsx @@ -1,30 +1,33 @@ -import React, { useState } from 'react'; -import styles from './style.module.css' +import React, { useState } from "react"; +import styles from "./style.module.css"; interface ToggleProps { - children: React.ReactNode; - state?: boolean; - onUpdate?: (isToggled: boolean) => void; + children: React.ReactNode; + state?: boolean; + onUpdate?: (isToggled: boolean) => void; } const Toggle: React.FC = ({ children, state, onUpdate }) => { - const [isToggled, setIsToggled] = useState(false); + const [isToggled, setIsToggled] = useState(false); - const getIsToggled = () => { - return state ?? isToggled; - } + const getIsToggled = () => { + return state ?? isToggled; + }; - const handleToggle = () => { - const newState = !getIsToggled(); - setIsToggled(newState); - onUpdate?.(newState); - }; + const handleToggle = () => { + const newState = !getIsToggled(); + setIsToggled(newState); + onUpdate?.(newState); + }; - return ( - - ); + return ( + + ); }; export default Toggle; diff --git a/webpage/app/toggleBag.tsx b/webpage/app/toggleBag.tsx index 431b035d..d59f0149 100644 --- a/webpage/app/toggleBag.tsx +++ b/webpage/app/toggleBag.tsx @@ -1,60 +1,71 @@ -import React, { useState } from 'react'; -import Toggle from './toggle'; -import { useEffect } from 'react'; +import React, { useState } from "react"; +import Toggle from "./toggle"; +import { useEffect } from "react"; interface ToggleBagProps { - title: string; - items: string[]; - all: boolean; - cols?: number; - mutualExclusions?: number[][]; - onChange?: (states: boolean[]) => void; + title: string; + items: string[]; + all: boolean; + cols?: number; + mutualExclusions?: number[][]; + onChange?: (states: boolean[]) => void; } -const ToggleBag: React.FC = ({ title, items, all, cols = 4, mutualExclusions = [], onChange }) => { - // Component logic goes here - const [states, setStates] = useState(Array(items.length + (all ? 1 : 0)).fill(false)); - useEffect(() => { - if (states.length !== items.length + (all ? 1 : 0)) { - setStates(Array(items.length + (all ? 1 : 0)).fill(false)); - } - }, [items]); +const ToggleBag: React.FC = ({ + title, + items, + all, + cols = 4, + mutualExclusions = [], + onChange, +}) => { + // Component logic goes here + const [states, setStates] = useState( + Array(items.length + (all ? 1 : 0)).fill(false), + ); + useEffect(() => { + if (states.length !== items.length + (all ? 1 : 0)) { + setStates(Array(items.length + (all ? 1 : 0)).fill(false)); + } + }, [items]); - const update = (index: number, state: boolean) => { - const newStates = [...states]; - for(const exclusion of mutualExclusions) { - if(exclusion.includes(index)) { - for(const i of exclusion) { - newStates[i + (all ? 1 : 0)] = false; - } - } + const update = (index: number, state: boolean) => { + const newStates = [...states]; + for (const exclusion of mutualExclusions) { + if (exclusion.includes(index)) { + for (const i of exclusion) { + newStates[i + (all ? 1 : 0)] = false; } - newStates[index + (all ? 1 : 0)] = state; - if(index == -1 && states.every((state) => state)) - newStates.fill(false); - else if(index == -1) - newStates.fill(true); - else if(all && newStates.every((state, index) => index == 0 || state)) - newStates[0] = true; - else if(all && newStates.every((state, index) => index == 0 || !state)) - newStates[0] = false; - setStates(newStates); - onChange?.(newStates); + } } + newStates[index + (all ? 1 : 0)] = state; + if (index == -1 && states.every((state) => state)) newStates.fill(false); + else if (index == -1) newStates.fill(true); + else if (all && newStates.every((state, index) => index == 0 || state)) + newStates[0] = true; + else if (all && newStates.every((state, index) => index == 0 || !state)) + newStates[0] = false; + setStates(newStates); + onChange?.(newStates); + }; - return ( - // JSX markup goes here -
-

{title}

-
- { - (all ? (["ALL"].concat(items)) : items).map((item, index) => ( - update(index - (all ? 1 : 0), state)} key={index}>{item} - )) - } -
-
- ); + return ( + // JSX markup goes here +
+

{title}

+
+ {(all ? ["ALL"].concat(items) : items).map((item, index) => ( + update(index - (all ? 1 : 0), state)} + key={index} + > + {item} + + ))} +
+
+ ); }; export default ToggleBag; diff --git a/webpage/types/react-graph.vis.d.ts b/webpage/types/react-graph.vis.d.ts index c471f0e5..c2163833 100644 --- a/webpage/types/react-graph.vis.d.ts +++ b/webpage/types/react-graph.vis.d.ts @@ -8,6 +8,6 @@ * Licensed under the MIT License */ -declare module 'react-graph-vis' { - export default class Graph extends React.Component {}; +declare module "react-graph-vis" { + export default class Graph extends React.Component {} } From ecf386557d550d2180fefd24a967f43e669a8183 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 2 Feb 2026 02:55:39 +0100 Subject: [PATCH 7/9] Improve pre-commit hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 48e35f27..69c397e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: - id: disallow-caps name: Disallow improper capitalization language: pygrep - entry: Numpy|Github|PyTest|Mqt|Tum + entry: '\b(?:Numpy|Github|PyTest|Mqt|Tum)\b' exclude: ^(.pre-commit-config.yaml|webpage/package-lock.json|.*.zip) priority: 0 From 9c8f573c3caae9a71431197c1cfe9c86794978fe Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 2 Feb 2026 03:11:11 +0100 Subject: [PATCH 8/9] Revert "Format TypeScript files with prettier" This reverts commit 767ec050e5a34b80a73a6f15b3f4205c50adaf60. --- .pre-commit-config.yaml | 2 +- webpage/app/edgeCollector.tsx | 141 ++++---- webpage/app/edgeDisplay.tsx | 32 +- webpage/app/graphView.tsx | 227 ++++++------- webpage/app/infoScreen.tsx | 44 +-- webpage/app/layout.tsx | 20 +- webpage/app/legal/page.tsx | 234 ++++--------- webpage/app/page.tsx | 292 +++++----------- webpage/app/pathPosIs.tsx | 41 +-- webpage/app/pathPosIsCollector.tsx | 175 +++++----- webpage/app/settings.ts | 521 +++++++++++++---------------- webpage/app/titledTextbox.tsx | 15 +- webpage/app/toggle.tsx | 41 ++- webpage/app/toggleBag.tsx | 109 +++--- webpage/types/react-graph.vis.d.ts | 4 +- 15 files changed, 757 insertions(+), 1141 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 69c397e7..8b19d431 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -109,7 +109,7 @@ repos: rev: v3.8.1 hooks: - id: prettier - types_or: [yaml, markdown, html, css, scss, javascript, json, ts, tsx] + types_or: [yaml, markdown, html, css, scss, javascript, json] priority: 1 ## Python linting using ruff diff --git a/webpage/app/edgeCollector.tsx b/webpage/app/edgeCollector.tsx index 25c0b488..3fff6b52 100644 --- a/webpage/app/edgeCollector.tsx +++ b/webpage/app/edgeCollector.tsx @@ -1,87 +1,76 @@ -import React, { useState } from "react"; -import Toggle from "./toggle"; -import EdgeDisplay from "./edgeDisplay"; +import React, { useState } from 'react'; +import Toggle from './toggle'; +import EdgeDisplay from './edgeDisplay'; interface EdgeCollectorProps { - title: string; - cols?: number; - allVertices: string[]; - onChange?: (edges: string[][]) => void; + title: string; + cols?: number; + allVertices: string[]; + onChange?: (edges: string[][]) => void; } -const EdgeCollector: React.FC = ({ - title, - cols = 4, - allVertices, - onChange, -}) => { - // Component logic goes here - const [items, setItems] = useState([]); +const EdgeCollector: React.FC = ({ title, cols = 4, allVertices, onChange}) => { + // Component logic goes here + const [items, setItems] = useState([]); - const itemAdded = (fromVertex: string, toVertex: string) => { - if (fromVertex === "" || toVertex === "") return; - if (items.some((item) => item[0] == fromVertex && item[1] == toVertex)) - return; - const newItems = [...items]; - newItems.push([fromVertex, toVertex]); - setItems(newItems); - onChange?.(newItems); - }; + const itemAdded = (fromVertex: string, toVertex: string) => { + if(fromVertex === "" || toVertex === "") + return; + if(items.some((item) => item[0] == fromVertex && item[1] == toVertex)) + return; + const newItems = [...items]; + newItems.push([fromVertex, toVertex]); + setItems(newItems); + onChange?.(newItems); + } - const itemRemoved = (fromVertex: string, toVertex: string) => { - const newItems = [...items]; - newItems.splice( - newItems.findIndex( - (item) => item[0] == fromVertex && item[1] == toVertex, - ), - 1, - ); - setItems(newItems); - onChange?.(newItems); - }; + const itemRemoved = (fromVertex: string, toVertex: string) => { + const newItems = [...items]; + newItems.splice(newItems.findIndex((item) => item[0] == fromVertex && item[1] == toVertex), 1); + setItems(newItems); + onChange?.(newItems); + } - const fromRef = React.createRef(); - const toRef = React.createRef(); + const fromRef = React.createRef(); + const toRef = React.createRef(); - return ( - // JSX markup goes here -
-

{title}

-
- - - - -
-
- {items.map((item, index) => ( - - itemRemoved?.(fromVertex, toVertex) - } - fromVertex={item[0]} - toVertex={item[1]} - key={index} - > - ))} -
-
- ); + return ( + // JSX markup goes here +
+

{title}

+
+ + + + +
+
+ { + items.map((item, index) => ( + itemRemoved?.(fromVertex, toVertex)} + fromVertex={item[0]} + toVertex={item[1]} + key={index}> + + )) + } +
+
+ ); }; export default EdgeCollector; diff --git a/webpage/app/edgeDisplay.tsx b/webpage/app/edgeDisplay.tsx index 0bb74567..3dcec8b4 100644 --- a/webpage/app/edgeDisplay.tsx +++ b/webpage/app/edgeDisplay.tsx @@ -1,26 +1,20 @@ -import React from "react"; + +import React from 'react'; interface EdgeDisplayProps { - fromVertex: string; - toVertex: string; - onClick?: (fromVertex: string, toVertex: string) => void; + fromVertex: string; + toVertex: string; + onClick?: (fromVertex: string, toVertex: string) => void; } -const EdgeDisplay: React.FC = ({ - fromVertex, - toVertex, - onClick, -}) => { - return ( -
onClick?.(fromVertex, toVertex)} - className={`text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`} - > - {fromVertex} - - {toVertex} -
- ); +const EdgeDisplay: React.FC = ({ fromVertex, toVertex, onClick }) => { + return ( +
onClick?.(fromVertex, toVertex)} className={`text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`}> + {fromVertex} + + {toVertex} +
+ ); }; export default EdgeDisplay; diff --git a/webpage/app/graphView.tsx b/webpage/app/graphView.tsx index a51d9ebe..b088f1f1 100644 --- a/webpage/app/graphView.tsx +++ b/webpage/app/graphView.tsx @@ -1,148 +1,127 @@ -import React, { useState, useRef, useEffect } from "react"; -import styles from "./style.module.css"; -import Script from "next/script"; -import Graph from "react-graph-vis"; +import React, { useState, useRef, useEffect } from 'react'; +import styles from './style.module.css' +import Script from 'next/script'; +import Graph from 'react-graph-vis'; const stringToAdjacencyMatrix = (str: string): number[][] | undefined => { - const lines = str.trim().split("\n"); - const matrix: number[][] = []; - for (const line of lines) { - const row: number[] = []; - for (const char of line.split(" ")) { - const result = parseFloat(char); - if (isNaN(result)) return undefined; - row.push(result); + const lines = str.trim().split('\n'); + const matrix: number[][] = []; + for(const line of lines) { + const row: number[] = []; + for(const char of line.split(" ")) { + const result = parseFloat(char); + if(isNaN(result)) + return undefined; + row.push(result); + } + matrix.push(row); } - matrix.push(row); - } - return matrix; -}; + return matrix; +} const adjacencyMatrixToString = (matrix: number[][]): string => { - let str = ""; - for (const row of matrix) { - for (const cell of row) { - str += cell + " "; + let str = ""; + for(const row of matrix) { + for(const cell of row) { + str += cell + " "; + } + str = str.trimEnd(); + str += "\n"; } - str = str.trimEnd(); - str += "\n"; - } - return str; -}; + return str; +} const adjacencyMatrixToDict = (matrix: number[][]): { [key: string]: any } => { - const dict: { [key: string]: any } = {}; - dict["nodes"] = []; - dict["edges"] = []; - for (let i = 0; i < matrix.length; i++) { - dict["nodes"].push({ id: i + 1, label: "" + (i + 1) }); - } - for (let i = 0; i < matrix.length; i++) { - for (let j = 0; j < matrix[i].length; j++) { - if (matrix[i][j] > 0) - dict["edges"].push({ - from: i + 1, - to: j + 1, - label: "" + matrix[i][j], - id: `${i + 1}-to-${j + 1}`, - }); + const dict: { [key: string]: any } = {}; + dict["nodes"] = []; + dict["edges"] = []; + for(let i = 0; i < matrix.length; i++) { + dict["nodes"].push({"id": i + 1, "label": "" + (i + 1)}); } - } - return dict; -}; + for(let i = 0; i < matrix.length; i++) { + for(let j = 0; j < matrix[i].length; j++) { + if(matrix[i][j] > 0) + dict["edges"].push({"from": i + 1, "to": j + 1, "label": ""+matrix[i][j], "id": `${i + 1}-to-${j + 1}`}); + } + } + return dict; +} interface GraphViewProps { - upload: boolean; - updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void; - initialAdjacencyMatrix?: number[][]; + upload: boolean, + updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void, + initialAdjacencyMatrix?: number[][] } -const GraphView: React.FC = ({ - upload, - updateAdjacencyMatrix, - initialAdjacencyMatrix, -}) => { - const [adjacencyMatrix, setAdjacencyMatrix] = useState( - initialAdjacencyMatrix ?? [], - ); - const [uploadMode, setUploadMode] = useState(false); - const textareaRef = useRef(null); - const [isClient, setIsClient] = useState(false); - useEffect(() => { - setIsClient(true); - }, []); +const GraphView: React.FC = ({ upload, updateAdjacencyMatrix, initialAdjacencyMatrix }) => { + const [adjacencyMatrix, setAdjacencyMatrix] = useState(initialAdjacencyMatrix ?? []); + const [uploadMode, setUploadMode] = useState(false); + const textareaRef = useRef(null); + const [isClient, setIsClient] = useState(false); - if (upload && !uploadMode) { - setUploadMode(true); - } + useEffect(() => { + setIsClient(true) + }, []); - const doOk = () => { - const textareaValue = textareaRef.current?.value; - const adj = stringToAdjacencyMatrix(textareaValue ?? ""); - if (adj !== undefined) { - setAdjacencyMatrix(adj); - updateAdjacencyMatrix(adj); + if(upload && !uploadMode) { + setUploadMode(true); } - setUploadMode(false); - }; - const doCancel = () => { - textareaRef.current!.value = adjacencyMatrixToString(adjacencyMatrix); - setUploadMode(false); - }; + const doOk = () => { + const textareaValue = textareaRef.current?.value; + const adj = stringToAdjacencyMatrix(textareaValue ?? ""); + if(adj !== undefined) { + setAdjacencyMatrix(adj); + updateAdjacencyMatrix(adj); + } + setUploadMode(false); + } + + const doCancel = () => { + textareaRef.current!.value = adjacencyMatrixToString(adjacencyMatrix); + setUploadMode(false); + } - const graph = adjacencyMatrixToDict(adjacencyMatrix); - const options = { - layout: { - hierarchical: false, - }, - nodes: { - color: { - background: "white", - border: "black", - }, - }, - edges: { - arrows: { - to: { enabled: true, scaleFactor: 1, type: "arrow" }, - }, - smooth: true, - font: { - align: "top", - }, - }, - }; + const graph = adjacencyMatrixToDict(adjacencyMatrix); + const options = { + layout: { + hierarchical: false, + }, + nodes: { + color: { + background: "white", + border: "black" + }, + }, + edges: { + arrows: { + to: { enabled: true, scaleFactor: 1, type: 'arrow', }, + }, + smooth: true, + font: { + align: "top" + } + }, + }; - return ( -
- {uploadMode || !isClient ? ( -
- -
- - -
+ return ( +
+ { + uploadMode || !isClient ? ( +
+ +
+ + +
+
+ ) : ( + + ) + }
- ) : ( - - )} -
- ); + ) }; export default GraphView; diff --git a/webpage/app/infoScreen.tsx b/webpage/app/infoScreen.tsx index 1d978edf..b15a4986 100644 --- a/webpage/app/infoScreen.tsx +++ b/webpage/app/infoScreen.tsx @@ -1,37 +1,15 @@ -import styles from "./style.module.css"; +import styles from './style.module.css' const InfoScreen: React.FC = () => { - return ( -
-

MQT QUBOMaker: Pathfinder

-

- This web app is a supporting GUI for the MQT QUBOMaker framework. It - allows users to define pathfinding problems using a set of constraints - that can be converted into a QUBO formulation by the framework. -

-

- Further details are given in the following paper: D. Rovara, N. - Quetschlich, and R. Wille{" "} - - "A Framework to Formulate Pathfinding Problems for Quantum - Computing" - - , arXiv, 2024 -

-

- The corresponding code can be found in the framework's{" "} - GitHub repository - . -

-

- MQT QUBOMaker is part of the{" "} - Munich Quantum Toolkit (MQT) - developed by the{" "} - Chair for Design Automation at - the Technical University of Munich. -

-
- ); -}; + return ( +
+

MQT QUBOMaker: Pathfinder

+

This web app is a supporting GUI for the MQT QUBOMaker framework. It allows users to define pathfinding problems using a set of constraints that can be converted into a QUBO formulation by the framework.

+

Further details are given in the following paper: D. Rovara, N. Quetschlich, and R. Wille "A Framework to Formulate Pathfinding Problems for Quantum Computing", arXiv, 2024

+

The corresponding code can be found in the framework's GitHub repository.

+

MQT QUBOMaker is part of the Munich Quantum Toolkit (MQT) developed by the Chair for Design Automation at the Technical University of Munich.

+
+ ); +} export default InfoScreen; diff --git a/webpage/app/layout.tsx b/webpage/app/layout.tsx index f6c23b85..33965c3a 100644 --- a/webpage/app/layout.tsx +++ b/webpage/app/layout.tsx @@ -1,23 +1,23 @@ -import type { Metadata } from "next"; -import { Inter } from "next/font/google"; -import "./globals.css"; -import Head from "next/head"; +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import './globals.css' +import Head from 'next/head' -const inter = Inter({ subsets: ["latin"] }); +const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { - title: "MQT QUBOMaker: Pathfinder", - description: "The UI assistant for MQT QUBOMaker", -}; + title: 'MQT QUBOMaker: Pathfinder', + description: 'The UI assistant for MQT QUBOMaker', +} export default function RootLayout({ children, }: { - children: React.ReactNode; + children: React.ReactNode }) { return ( {children} - ); + ) } diff --git a/webpage/app/legal/page.tsx b/webpage/app/legal/page.tsx index a6db4c76..0ff50961 100644 --- a/webpage/app/legal/page.tsx +++ b/webpage/app/legal/page.tsx @@ -1,176 +1,90 @@ -"use client"; +'use client' -import Image from "next/image"; +import Image from 'next/image' export default function Legal() { - return ( -
- -
-
-
- Herausgeber -
-
Technische Universität München
-
Arcisstraße 21
-
80333 München
-
Telefon: +49 89 289-01
-
poststelle@tum.de
-
+ return ( +
+ +
+
+
Herausgeber
+
Technische Universität München
+
Arcisstraße 21
+
80333 München
+
Telefon: +49 89 289-01
+
poststelle@tum.de
+
-
-
- Rechtsform und Vertretung -
-
- Die Technische Universität München ist eine Körperschaft des - Öffentlichen Rechts und staatliche Einrichtung (Art. 11 Abs. 1 - BayHSchG). Sie wird gesetzlich vertreten durch den Präsidenten Prof. - Dr. Thomas F. Hofmann. -
-
+
+
Rechtsform und Vertretung
+
Die Technische Universität München ist eine Körperschaft des Öffentlichen Rechts und staatliche Einrichtung (Art. 11 Abs. 1 BayHSchG). Sie wird gesetzlich vertreten durch den Präsidenten Prof. Dr. Thomas F. Hofmann.
+
-
-
- Zuständige Aufsichtsbehörde -
-
Bayerisches Staatsministerium für Wissenschaft und Kunst
-
+
+
Zuständige Aufsichtsbehörde
+
Bayerisches Staatsministerium für Wissenschaft und Kunst
+
-
-
- Umsatzsteueridentifikationnummer -
-
DE811193231 (gemäß § 27a Umsatzsteuergesetz)
-
+
+
Umsatzsteueridentifikationnummer
+
DE811193231 (gemäß § 27a Umsatzsteuergesetz)
+
-
-
- Inhaltlich verantwortlich -
-
Prof. Dr. Robert Wille
-
Arcisstr. 21
-
80333 München
-
E-Mail: robert.wille(at)tum.de
-
+
+
Inhaltlich verantwortlich
+
Prof. Dr. Robert Wille
+
Arcisstr. 21
+
80333 München
+
E-Mail: robert.wille(at)tum.de
+
-
- Namentlich gekennzeichnete Internetseiten geben die Auffassungen und - Erkenntnisse der genannten Personen wieder. -
+
+ Namentlich gekennzeichnete Internetseiten geben die Auffassungen und Erkenntnisse der genannten Personen wieder. +
-
-
- Nutzungsbedingungen -
-
- Texte, Bilder, Grafiken sowie die Gestaltung dieses Webauftritts - können dem Urheberrecht unterliegen. Nicht urheberrechtlich - geschützt sind nach § 5 des Urheberrechtsgesetz (UrhG) -
-
    -
  • - - Gesetze, Verordnungen, amtliche Erlasse und Bekanntmachungen - sowie Entscheidungen und amtlich verfasste Leitsätze zu - Entscheidungen und -
  • -
  • - - andere amtliche Werke, die im amtlichen Interesse zur - allgemeinen Kenntnisnahme veröffentlicht worden sind, mit der - Einschränkung, dass die Bestimmungen über Änderungsverbot und - Quellenangabe in § 62 Abs. 1 bis 3 und § 63 Abs. 1 und 2 UrhG - entsprechend anzuwenden sind. -
  • -
-
+
+
Nutzungsbedingungen
+
Texte, Bilder, Grafiken sowie die Gestaltung dieses Webauftritts können dem Urheberrecht unterliegen. Nicht urheberrechtlich geschützt sind nach § 5 des Urheberrechtsgesetz (UrhG)
+
    +
  • - Gesetze, Verordnungen, amtliche Erlasse und Bekanntmachungen sowie Entscheidungen und amtlich verfasste Leitsätze zu Entscheidungen und
  • +
  • - andere amtliche Werke, die im amtlichen Interesse zur allgemeinen Kenntnisnahme veröffentlicht worden sind, mit der Einschränkung, dass die Bestimmungen über Änderungsverbot und Quellenangabe in § 62 Abs. 1 bis 3 und § 63 Abs. 1 und 2 UrhG entsprechend anzuwenden sind.
  • +
+
-
- Als Privatperson dürfen Sie urheberrechtlich geschütztes Material zum - privaten und sonstigen eigenen Gebrauch im Rahmen des § 53 UrhG - verwenden. Eine Vervielfältigung oder Verwendung urheberrechtlich - geschützten Materials dieser Seiten oder Teilen davon in anderen - elektronischen oder gedruckten Publikationen und deren - Veröffentlichung ist nur mit unserer Einwilligung gestattet. Diese - Einwilligung erteilen auf Anfrage die für den Inhalt Verantwortlichen. - Der Nachdruck und die Auswertung von Pressemitteilungen und Reden sind - mit Quellenangabe allgemein gestattet. -
+
+ Als Privatperson dürfen Sie urheberrechtlich geschütztes Material zum privaten und sonstigen eigenen Gebrauch im Rahmen des § 53 UrhG verwenden. Eine Vervielfältigung oder Verwendung urheberrechtlich geschützten Materials dieser Seiten oder Teilen davon in anderen elektronischen oder gedruckten Publikationen und deren Veröffentlichung ist nur mit unserer Einwilligung gestattet. Diese Einwilligung erteilen auf Anfrage die für den Inhalt Verantwortlichen. Der Nachdruck und die Auswertung von Pressemitteilungen und Reden sind mit Quellenangabe allgemein gestattet. +
-
- Weiterhin können Texte, Bilder, Grafiken und sonstige Dateien ganz - oder teilweise dem Urheberrecht Dritter unterliegen. Auch über das - Bestehen möglicher Rechte Dritter geben Ihnen die für den Inhalt - Verantwortlichen nähere Auskünfte. -
+
+ Weiterhin können Texte, Bilder, Grafiken und sonstige Dateien ganz oder teilweise dem Urheberrecht Dritter unterliegen. Auch über das Bestehen möglicher Rechte Dritter geben Ihnen die für den Inhalt Verantwortlichen nähere Auskünfte. +
-
-
- Haftungsausschluss -
-
- Alle in diesem Webauftritt bereitgestellten Informationen haben wir - nach bestem Wissen und Gewissen erarbeitet und geprüft. Eine Gewähr - für die jederzeitige Aktualität, Richtigkeit, Vollständigkeit und - Verfügbarkeit der bereit gestellten Informationen können wir - allerdings nicht übernehmen. In Vertragsverhältnis mit den Nutzern - des Webauftritts kommt nicht zustande. -
-
+
+
Haftungsausschluss
+
+ Alle in diesem Webauftritt bereitgestellten Informationen haben wir nach bestem Wissen und Gewissen erarbeitet und geprüft. Eine Gewähr für die jederzeitige Aktualität, Richtigkeit, Vollständigkeit und Verfügbarkeit der bereit gestellten Informationen können wir allerdings nicht übernehmen. In Vertragsverhältnis mit den Nutzern des Webauftritts kommt nicht zustande. +
+
-
- Wir haften nicht für Schäden, die durch die Nutzung dieses - Webauftritts entstehen. Dieser Haftungsausschluss gilt nicht, soweit - die Vorschriften des § 839 BGB (Haftung bei Amtspflichtverletzung) - einschlägig sind. Für etwaige Schäden, die beim Aufrufen oder - Herunterladen von Daten durch Schadsoftware oder der Installation oder - Nutzung von Software verursacht werden, übernehmen wir keine Haftung. -
+
+ Wir haften nicht für Schäden, die durch die Nutzung dieses Webauftritts entstehen. Dieser Haftungsausschluss gilt nicht, soweit die Vorschriften des § 839 BGB (Haftung bei Amtspflichtverletzung) einschlägig sind. Für etwaige Schäden, die beim Aufrufen oder Herunterladen von Daten durch Schadsoftware oder der Installation oder Nutzung von Software verursacht werden, übernehmen wir keine Haftung. +
-
-
- Links -
-
- Von unseren eigenen Inhalten sind Querverweise ("Links") - auf die Webseiten anderer Anbieter zu unterscheiden. Durch diese - Links ermöglichen wir lediglich den Zugang zur Nutzung fremder - Inhalte nach § 8 Telemediengesetz. Bei der erstmaligen Verknüpfung - mit diesen Internetangeboten haben wir diese fremden Inhalte - daraufhin überprüft, ob durch sie eine mögliche zivilrechtliche oder - strafrechtliche Verantwortlichkeit ausgelöst wird. Wir können diese - fremden Inhalte aber nicht ständig auf Veränderungen überprüfen und - daher auch keine Verantwortung dafür übernehmen. Für illegale, - fehlerhafte oder unvollständige Inhalte und insbesondere für - Schäden, die aus der Nutzung oder Nichtnutzung von Informationen - Dritter entstehen, haftet allein der jeweilige Anbieter der Seite. -
-
+
+
Links
+
+ Von unseren eigenen Inhalten sind Querverweise ("Links") auf die Webseiten anderer Anbieter zu unterscheiden. Durch diese Links ermöglichen wir lediglich den Zugang zur Nutzung fremder Inhalte nach § 8 Telemediengesetz. Bei der erstmaligen Verknüpfung mit diesen Internetangeboten haben wir diese fremden Inhalte daraufhin überprüft, ob durch sie eine mögliche zivilrechtliche oder strafrechtliche Verantwortlichkeit ausgelöst wird. Wir können diese fremden Inhalte aber nicht ständig auf Veränderungen überprüfen und daher auch keine Verantwortung dafür übernehmen. Für illegale, fehlerhafte oder unvollständige Inhalte und insbesondere für Schäden, die aus der Nutzung oder Nichtnutzung von Informationen Dritter entstehen, haftet allein der jeweilige Anbieter der Seite. +
+
-
-

-
-
-
- ); +


+
+
+ ); } diff --git a/webpage/app/page.tsx b/webpage/app/page.tsx index 69a44c45..05fc1f16 100644 --- a/webpage/app/page.tsx +++ b/webpage/app/page.tsx @@ -1,30 +1,27 @@ -"use client"; +'use client' -import Image from "next/image"; -import Toggle from "./toggle"; -import ToggleBag from "./toggleBag"; -import EdgeDisplay from "./edgeDisplay"; -import EdgeCollector from "./edgeCollector"; -import GraphView from "./graphView"; -import { useEffect, useRef, useState } from "react"; -import TitledTextbox from "./titledTextbox"; -import Settings from "./settings"; -import PathPosIsCollector from "./pathPosIsCollector"; -import InfoScreen from "./infoScreen"; +import Image from 'next/image' +import Toggle from './toggle' +import ToggleBag from './toggleBag' +import EdgeDisplay from './edgeDisplay' +import EdgeCollector from './edgeCollector' +import GraphView from './graphView' +import { useEffect, useRef, useState } from 'react' +import TitledTextbox from './titledTextbox' +import Settings from './settings' +import PathPosIsCollector from './pathPosIsCollector' +import InfoScreen from './infoScreen' function download(filename: string, text: string) { - if (text === "") { + if(text === "") { alert("Please select an encoding!"); return; } - var element = document.createElement("a"); - element.setAttribute( - "href", - "data:text/plain;charset=utf-8," + encodeURIComponent(text), - ); - element.setAttribute("download", filename); + var element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', filename); - element.style.display = "none"; + element.style.display = 'none'; document.body.appendChild(element); element.click(); @@ -33,233 +30,94 @@ function download(filename: string, text: string) { } export default function Home() { - const [settings, setSettings] = useState(new Settings()); + const [settings, setSettings] = useState(new Settings()) const [doUpload, setDoUpload] = useState(false); - const [adjacencyMatrix, setAdjacencyMatrix] = useState([ - [0, 4, 2, 0], + const [adjacencyMatrix, setAdjacencyMatrix] = useState([[0, 4, 2, 0], [3, 0, 0, 2], [2, 0, 0, 5], - [2, 0, 5, 0], - ]); - const updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void = ( - adjacencyMatrix, - ) => { + [2, 0, 5, 0] + ]); + const updateAdjacencyMatrix: (adjacencyMatrix: number[][]) => void = (adjacencyMatrix) => { setAdjacencyMatrix(adjacencyMatrix); setSettings(settings.setMaxPathLength(adjacencyMatrix.length)); - }; + } useEffect(() => { - if (doUpload) setDoUpload(false); + if(doUpload) + setDoUpload(false); }, [doUpload]); useEffect(() => { - setVertices( - Array.from(Array(adjacencyMatrix.length).keys()).map((i) => - (i + 1).toFixed(0), - ), - ); - }, [adjacencyMatrix]); + setVertices(Array.from(Array(adjacencyMatrix.length).keys()).map(i => (i + 1).toFixed(0))); + + }, [adjacencyMatrix]) - const [vertices, setVertices] = useState( - Array.from(Array(adjacencyMatrix.length).keys()).map((i) => - (i + 1).toFixed(0), - ), - ); + const [vertices, setVertices] = useState(Array.from(Array(adjacencyMatrix.length).keys()).map(i => (i + 1).toFixed(0))); const [showInfo, setShowInfo] = useState(false); return (
- {showInfo ? ( - - ) : ( - - )} + { + showInfo ? : + }
- - - + + +
- setSettings(settings.setEncoding(states))} - > + setSettings(settings.setEncoding(states))}>
- { - const newNum = parseInt(n); - if (!isNaN(newNum) && newNum > 0) - setSettings(settings.setNPaths(newNum)); - }} - > - { - const newNum = parseInt(n); - if (!isNaN(newNum) && newNum > 0) - setSettings(settings.setMaxPathLength(newNum)); - }} - > + { + const newNum = parseInt(n); + if(!isNaN(newNum) && newNum > 0) + setSettings(settings.setNPaths(newNum)); + }}> + { + const newNum = parseInt(n); + if(!isNaN(newNum) && newNum > 0) + setSettings(settings.setMaxPathLength(newNum)); + }}>
- - setSettings(settings.setGeneralSettings(states)) - } - > - - setSettings(settings.setExactlyOnceVertices(states)) - } - all={true} - title="The following vertices must appear exactly once in each path" - items={vertices} - > - - setSettings(settings.setAtLeastOnceVertices(states)) - } - all={true} - title="The following vertices must appear at least once in each path" - items={vertices} - > - - setSettings(settings.setAtMostOnceVertices(states)) - } - all={true} - title="The following vertices must appear at most once in each path" - items={vertices} - > + setSettings(settings.setGeneralSettings(states))}> + setSettings(settings.setExactlyOnceVertices(states))} all={true} title='The following vertices must appear exactly once in each path' items={vertices}> + setSettings(settings.setAtLeastOnceVertices(states))} all={true} title='The following vertices must appear at least once in each path' items={vertices}> + setSettings(settings.setAtMostOnceVertices(states))} all={true} title='The following vertices must appear at most once in each path' items={vertices}> - - setSettings(settings.setShareNoVertices(states)) - } - all={true} - title="The following paths may not share vertices" - items={Array(settings.nPaths) - .fill(0) - .map((_, i) => i + 1 + "")} - > - - setSettings(settings.setShareNoVertices(states)) - } - all={true} - title="The following paths may not share edges" - items={Array(settings.nPaths) - .fill(0) - .map((_, i) => i + 1 + "")} - > + setSettings(settings.setShareNoVertices(states))} all={true} title='The following paths may not share vertices' items={Array(settings.nPaths).fill(0).map((_, i) => (i + 1) + "")}> + setSettings(settings.setShareNoVertices(states))} all={true} title='The following paths may not share edges' items={Array(settings.nPaths).fill(0).map((_, i) => (i + 1) + "")}>
- - setSettings(settings.setPathPositionIs(entries)) - } - title="Vertex positions" - nPaths={settings.nPaths} - pathLength={settings.maxPathLength} - allVertices={vertices} - > - - setSettings(settings.setExactlyOnceEdges(edges)) - } - title="The following edges must appear exactly once in each path" - allVertices={vertices} - > - - setSettings(settings.setAtLeastOnceEdges(edges)) - } - title="The following edges must appear at least once in each path" - allVertices={vertices} - > - - setSettings(settings.setAtMostOnceEdges(edges)) - } - title="The following edges must appear at most once in each path" - allVertices={vertices} - > + setSettings(settings.setPathPositionIs(entries))} title="Vertex positions" nPaths={settings.nPaths} pathLength={settings.maxPathLength} allVertices={vertices}> + setSettings(settings.setExactlyOnceEdges(edges))} title="The following edges must appear exactly once in each path" allVertices={vertices}> + setSettings(settings.setAtLeastOnceEdges(edges))} title="The following edges must appear at least once in each path" allVertices={vertices}> + setSettings(settings.setAtMostOnceEdges(edges))} title="The following edges must appear at most once in each path" allVertices={vertices}> + - setSettings(settings.setPrecedence(edges))} - title="The following precedence constraints must be fulfilled" - allVertices={vertices} - > + setSettings(settings.setPrecedence(edges))} title="The following precedence constraints must be fulfilled" allVertices={vertices}>
- ); + ) } diff --git a/webpage/app/pathPosIs.tsx b/webpage/app/pathPosIs.tsx index af5f6e58..65902e61 100644 --- a/webpage/app/pathPosIs.tsx +++ b/webpage/app/pathPosIs.tsx @@ -1,31 +1,24 @@ -import React from "react"; + +import React from 'react'; interface PathPosIsProps { - path: number; - position: number; - vertex: string; - onClick?: (path: number, position: number, vertex: string) => void; + path: number; + position: number; + vertex: string; + onClick?: (path: number, position: number, vertex: string) => void; } -const PathPosIs: React.FC = ({ - path, - position, - vertex, - onClick, -}) => { - return ( -
onClick?.(path, position, vertex)} - className={`justify-around inline-flex flex-wrap-nowrap text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`} - > - Path - {path} - position - {position} - is: - {vertex} -
- ); +const PathPosIs: React.FC = ({ path, position, vertex, onClick }) => { + return ( +
onClick?.(path, position, vertex)} className={`justify-around inline-flex flex-wrap-nowrap text-center border-2 rounded-sm p-1 ${onClick !== undefined ? "cursor-pointer" : ""}`}> + Path + {path} + position + {position} + is: + {vertex} +
+ ); }; export default PathPosIs; diff --git a/webpage/app/pathPosIsCollector.tsx b/webpage/app/pathPosIsCollector.tsx index d5bc236d..d61e1128 100644 --- a/webpage/app/pathPosIsCollector.tsx +++ b/webpage/app/pathPosIsCollector.tsx @@ -1,108 +1,89 @@ -import React, { useState } from "react"; -import Toggle from "./toggle"; -import PathPosIs from "./pathPosIs"; +import React, { useState } from 'react'; +import Toggle from './toggle'; +import PathPosIs from './pathPosIs'; interface PathPosIsCollectorProps { - title: string; - cols?: number; - allVertices: string[]; - nPaths: number; - pathLength: number; - onChange?: (items: string[][]) => void; + title: string; + cols?: number; + allVertices: string[]; + nPaths: number; + pathLength: number; + onChange?: (items: string[][]) => void; } -const PathPosIsCollector: React.FC = ({ - title, - cols = 2, - allVertices, - nPaths, - pathLength, - onChange, -}) => { - // Component logic goes here - const [items, setItems] = useState([]); +const PathPosIsCollector: React.FC = ({ title, cols = 2, allVertices, nPaths, pathLength, onChange}) => { + // Component logic goes here + const [items, setItems] = useState([]); - const itemAdded = (path: string, position: string, vertex: string) => { - if (vertex === "") return; - if ( - items.some( - (item) => item[0] == path && item[1] == position && item[2] == vertex, - ) - ) - return; - const newItems = [...items]; - newItems.push([path, position, vertex]); - setItems(newItems); - onChange?.(newItems); - }; + const itemAdded = (path: string, position: string, vertex: string) => { + if(vertex === "") + return; + if(items.some((item) => item[0] == path && item[1] == position && item[2] == vertex)) + return; + const newItems = [...items]; + newItems.push([path, position, vertex]); + setItems(newItems); + onChange?.(newItems); + } - const itemRemoved = (path: string, position: string, vertex: string) => { - const newItems = [...items]; - newItems.splice( - newItems.findIndex( - (item) => item[0] == path && item[1] == position && item[2] == vertex, - ), - 1, - ); - setItems(newItems); - onChange?.(newItems); - }; + const itemRemoved = (path: string, position: string, vertex: string) => { + const newItems = [...items]; + newItems.splice(newItems.findIndex((item) => item[0] == path && item[1] == position && item[2] == vertex), 1); + setItems(newItems); + onChange?.(newItems); + } - const pathRef = React.createRef(); - const posRef = React.createRef(); - const vertexRef = React.createRef(); + const pathRef = React.createRef(); + const posRef = React.createRef(); + const vertexRef = React.createRef(); - return ( - // JSX markup goes here -
-

{title}

-
- Path - - position - - is - - -
-
- {items.map((item, index) => ( - - itemRemoved?.(path + "", position + "", vertex) - } - path={parseInt(item[0])} - position={parseInt(item[1])} - vertex={item[2]} - key={index} - > - ))} -
-
- ); + return ( + // JSX markup goes here +
+

{title}

+
+ Path + + position + + is + + +
+
+ { + items.map((item, index) => ( + itemRemoved?.(path + "", position + "", vertex)} + path={parseInt(item[0])} + position={parseInt(item[1])} + vertex={item[2]} + key={index}> + + )) + } +
+
+ ); }; export default PathPosIsCollector; diff --git a/webpage/app/settings.ts b/webpage/app/settings.ts index cd0ee50c..348e0130 100644 --- a/webpage/app/settings.ts +++ b/webpage/app/settings.ts @@ -9,321 +9,274 @@ */ class Settings { - encoding: number = -1; - nPaths: number = 1; - maxPathLength: number = 4; - loop: boolean = false; - minimizeWeight: boolean = false; - maximizeWeight: boolean = false; + encoding: number = -1; + nPaths: number = 1; + maxPathLength: number = 4; - exactlyOnceVertices: number[] = []; - atLeastOnceVertices: number[] = []; - atMostOnceVertices: number[] = []; + loop: boolean = false; + minimizeWeight: boolean = false; + maximizeWeight: boolean = false; - exactlyOnceEdges: string[][] = []; - atLeastOnceEdges: string[][] = []; - atMostOnceEdges: string[][] = []; + exactlyOnceVertices: number[] = []; + atLeastOnceVertices: number[] = []; + atMostOnceVertices: number[] = []; - pathPositionIs: string[][] = []; + exactlyOnceEdges: string[][] = []; + atLeastOnceEdges: string[][] = []; + atMostOnceEdges: string[][] = []; - precedences: string[][] = []; + pathPositionIs: string[][] = []; - shareNoEdges: number[] = []; - shareNoVertices: number[] = []; + precedences: string[][] = []; - setEncoding(encoding: boolean[]) { - const clone = this.clone(); - clone.encoding = encoding.indexOf(true); - return clone; - } + shareNoEdges: number[] = []; + shareNoVertices: number[] = []; - setNPaths(nPaths: number) { - const clone = this.clone(); - clone.nPaths = nPaths; - return clone; - } - - setMaxPathLength(maxPathLength: number) { - const clone = this.clone(); - clone.maxPathLength = maxPathLength; - return clone; - } - - setGeneralSettings(settings: boolean[]) { - const clone = this.clone(); - clone.loop = settings[0]; - clone.minimizeWeight = settings[1]; - clone.maximizeWeight = settings[2]; - return clone; - } - - setExactlyOnceVertices(exactlyOnceVertices: boolean[]) { - const clone = this.clone(); - clone.exactlyOnceVertices = exactlyOnceVertices - .slice(1) - .map((value, index) => (value ? index : -1)) - .filter((value) => value !== -1); - return clone; - } - - setAtLeastOnceVertices(atLeastOnceVertices: boolean[]) { - const clone = this.clone(); - clone.atLeastOnceVertices = atLeastOnceVertices - .slice(1) - .map((value, index) => (value ? index : -1)) - .filter((value) => value !== -1); - return clone; - } - - setAtMostOnceVertices(atMostOnceVertices: boolean[]) { - const clone = this.clone(); - clone.atMostOnceVertices = atMostOnceVertices - .slice(1) - .map((value, index) => (value ? index : -1)) - .filter((value) => value !== -1); - return clone; - } - - setShareNoVertices(shareNoVertices: boolean[]) { - const clone = this.clone(); - clone.shareNoVertices = shareNoVertices - .slice(1) - .map((value, index) => (value ? index : -1)) - .filter((value) => value !== -1); - return clone; - } - - setShareNoEdges(shareNoEdges: boolean[]) { - const clone = this.clone(); - clone.shareNoEdges = shareNoEdges - .slice(1) - .map((value, index) => (value ? index : -1)) - .filter((value) => value !== -1); - return clone; - } - - setPathPositionIs(pathPositionIs: string[][]) { - const clone = this.clone(); - clone.pathPositionIs = pathPositionIs; - return clone; - } - - setExactlyOnceEdges(exactlyOnceEdges: string[][]) { - const clone = this.clone(); - clone.exactlyOnceEdges = exactlyOnceEdges; - return clone; - } - - setAtLeastOnceEdges(atLeastOnceEdges: string[][]) { - const clone = this.clone(); - clone.atLeastOnceEdges = atLeastOnceEdges; - return clone; - } + setEncoding(encoding: boolean[]) { + const clone = this.clone(); + clone.encoding = encoding.indexOf(true); + return clone; + } - setAtMostOnceEdges(atMostOnceEdges: string[][]) { - const clone = this.clone(); - clone.atMostOnceEdges = atMostOnceEdges; - return clone; - } + setNPaths(nPaths: number) { + const clone = this.clone(); + clone.nPaths = nPaths; + return clone; + } - setPrecedence(precedences: string[][]) { - const clone = this.clone(); - clone.precedences = precedences; - return clone; - } + setMaxPathLength(maxPathLength: number) { + const clone = this.clone(); + clone.maxPathLength = maxPathLength; + return clone; + } - clone() { - let clone = new Settings(); - clone.encoding = this.encoding; - clone.nPaths = this.nPaths; - clone.maxPathLength = this.maxPathLength; - clone.loop = this.loop; - clone.minimizeWeight = this.minimizeWeight; - clone.maximizeWeight = this.maximizeWeight; - clone.exactlyOnceVertices = this.exactlyOnceVertices.slice(); - clone.atLeastOnceVertices = this.atLeastOnceVertices.slice(); - clone.atMostOnceVertices = this.atMostOnceVertices.slice(); - clone.exactlyOnceEdges = this.exactlyOnceEdges.map((value) => - value.slice(), - ); - clone.atLeastOnceEdges = this.atLeastOnceEdges.map((value) => - value.slice(), - ); - clone.atMostOnceEdges = this.atMostOnceEdges.map((value) => value.slice()); - clone.precedences = this.precedences.map((value) => value.slice()); - clone.shareNoEdges = this.shareNoEdges.slice(); - clone.shareNoVertices = this.shareNoVertices.slice(); - clone.pathPositionIs = this.pathPositionIs.map((value) => value.slice()); - return clone; - } + setGeneralSettings(settings: boolean[]) { + const clone = this.clone(); + clone.loop = settings[0]; + clone.minimizeWeight = settings[1]; + clone.maximizeWeight = settings[2]; + return clone; + } - encodingToString() { - if (this.encoding === -1) { - throw new Error("Encoding not set"); + setExactlyOnceVertices(exactlyOnceVertices: boolean[]) { + const clone = this.clone(); + clone.exactlyOnceVertices = exactlyOnceVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); + return clone; } - if (this.encoding == 0) { - return "ONE_HOT"; + + setAtLeastOnceVertices(atLeastOnceVertices: boolean[]) { + const clone = this.clone(); + clone.atLeastOnceVertices = atLeastOnceVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); + return clone; } - if (this.encoding == 1) { - return "UNARY"; + + setAtMostOnceVertices(atMostOnceVertices: boolean[]) { + const clone = this.clone(); + clone.atMostOnceVertices = atMostOnceVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); + return clone; } - if (this.encoding == 2) { - return "BINARY"; + + setShareNoVertices(shareNoVertices: boolean[]) { + const clone = this.clone(); + clone.shareNoVertices = shareNoVertices.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); + return clone; } - } - toJson() { - if (this.encoding === -1) { - throw new Error("Encoding not set"); + setShareNoEdges(shareNoEdges: boolean[]) { + const clone = this.clone(); + clone.shareNoEdges = shareNoEdges.slice(1).map((value, index) => value ? index : -1).filter(value => value !== -1); + return clone; } - const object: { [key: string]: any } = {}; - object["settings"] = { - encoding: this.encodingToString(), - n_paths: this.nPaths, - max_path_length: this.maxPathLength, - loops: this.loop, - }; - if (this.minimizeWeight && this.maximizeWeight) { - throw new Error("Cannot minimize and maximize weight at the same time"); - } else if (this.minimizeWeight) { - object["objective_function"] = { - type: "MinimizePathLength", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - }; - } else if (this.maximizeWeight) { - object["objective_function"] = { - type: "MaximizePathLength", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - }; + + setPathPositionIs(pathPositionIs: string[][]) { + const clone = this.clone(); + clone.pathPositionIs = pathPositionIs; + return clone; } - const constraints: { [key: string]: any }[] = []; - constraints.push({ - type: "PathIsValid", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - }); - //--------------------- Vertices --------------------- - if (this.exactlyOnceVertices.length > 0) { - constraints.push({ - type: "PathContainsVerticesExactlyOnce", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - vertices: this.exactlyOnceVertices.map((value) => value + 1), - }); + setExactlyOnceEdges(exactlyOnceEdges: string[][]) { + const clone = this.clone(); + clone.exactlyOnceEdges = exactlyOnceEdges; + return clone; } - if (this.atLeastOnceVertices.length > 0) { - constraints.push({ - type: "PathContainsVerticesAtLeastOnce", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - vertices: this.atLeastOnceVertices.map((value) => value + 1), - }); + + setAtLeastOnceEdges(atLeastOnceEdges: string[][]) { + const clone = this.clone(); + clone.atLeastOnceEdges = atLeastOnceEdges; + return clone; } - if (this.atMostOnceVertices.length > 0) { - constraints.push({ - type: "PathContainsVerticesAtMostOnce", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - vertices: this.atMostOnceVertices.map((value) => value + 1), - }); + + setAtMostOnceEdges(atMostOnceEdges: string[][]) { + const clone = this.clone(); + clone.atMostOnceEdges = atMostOnceEdges; + return clone; } - //--------------------- Edges --------------------- - if (this.exactlyOnceEdges.length > 0) { - constraints.push({ - type: "PathContainsEdgesExactlyOnce", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - edges: this.exactlyOnceEdges.map((pair) => - pair.map((val) => parseInt(val)), - ), - }); + + setPrecedence(precedences: string[][]) { + const clone = this.clone(); + clone.precedences = precedences; + return clone; } - if (this.atLeastOnceEdges.length > 0) { - constraints.push({ - type: "PathContainsEdgesAtLeastOnce", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - edges: this.atLeastOnceEdges.map((pair) => - pair.map((val) => parseInt(val)), - ), - }); + + clone() { + let clone = new Settings(); + clone.encoding = this.encoding; + clone.nPaths = this.nPaths; + clone.maxPathLength = this.maxPathLength; + clone.loop = this.loop; + clone.minimizeWeight = this.minimizeWeight; + clone.maximizeWeight = this.maximizeWeight; + clone.exactlyOnceVertices = this.exactlyOnceVertices.slice(); + clone.atLeastOnceVertices = this.atLeastOnceVertices.slice(); + clone.atMostOnceVertices = this.atMostOnceVertices.slice(); + clone.exactlyOnceEdges = this.exactlyOnceEdges.map(value => value.slice()); + clone.atLeastOnceEdges = this.atLeastOnceEdges.map(value => value.slice()); + clone.atMostOnceEdges = this.atMostOnceEdges.map(value => value.slice()); + clone.precedences = this.precedences.map(value => value.slice()); + clone.shareNoEdges = this.shareNoEdges.slice(); + clone.shareNoVertices = this.shareNoVertices.slice(); + clone.pathPositionIs = this.pathPositionIs.map(value => value.slice()); + return clone; } - if (this.atMostOnceEdges.length > 0) { - constraints.push({ - type: "PathContainsEdgesAtMostOnce", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - edges: this.atMostOnceEdges.map((pair) => - pair.map((val) => parseInt(val)), - ), - }); + + encodingToString() { + if(this.encoding === -1) { + throw new Error("Encoding not set"); + } + if(this.encoding == 0) { + return "ONE_HOT"; + } + if(this.encoding == 1) { + return "UNARY"; + } + if(this.encoding == 2) { + return "BINARY"; + } } - if (this.pathPositionIs.length > 0) { - const keyValue: [string[], string][] = this.pathPositionIs.map( - (value) => [[value[0], value[1]], value[2]], - ); - const map = new Map(); - for (const entry of keyValue) { - if (!map.has(JSON.stringify(entry[0]))) { - map.set(JSON.stringify(entry[0]), []); + + toJson() { + if(this.encoding === -1) { + throw new Error("Encoding not set"); + } + const object: { [key: string]: any} = {}; + object["settings"] = { + "encoding": this.encodingToString(), + "n_paths": this.nPaths, + "max_path_length": this.maxPathLength, + "loops": this.loop, + }; + if(this.minimizeWeight && this.maximizeWeight) { + throw new Error("Cannot minimize and maximize weight at the same time"); + } else if(this.minimizeWeight) { + object["objective_function"] = { + "type": "MinimizePathLength", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1) + }; + } else if(this.maximizeWeight) { + object["objective_function"] = { + "type": "MaximizePathLength", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1) + }; } - map.get(JSON.stringify(entry[0]))!.push(entry[1]); - } - console.log(map.keys()); - for (const entry of map.keys()) { - const entry_array = JSON.parse(entry); + const constraints: { [key: string]: any}[] = []; constraints.push({ - type: "PathPositionIs", - position: parseInt(entry_array[1]), - vertices: map.get(entry)!.map((vertex) => parseInt(vertex)), - path_id: parseInt(entry_array[0]), + "type": "PathIsValid", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), }); - } - } - if (this.precedences.length > 0) { - constraints.push({ - type: "PrecedenceConstraint", - precedences: this.precedences.map((value) => ({ - before: parseInt(value[0]), - after: parseInt(value[1]), - })), - }); - } + //--------------------- Vertices --------------------- + if(this.exactlyOnceVertices.length > 0) { + constraints.push({ + "type": "PathContainsVerticesExactlyOnce", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + "vertices": this.exactlyOnceVertices.map(value => value + 1) + }); + } + if(this.atLeastOnceVertices.length > 0) { + constraints.push({ + "type": "PathContainsVerticesAtLeastOnce", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + "vertices": this.atLeastOnceVertices.map(value => value + 1) + }); + } + if(this.atMostOnceVertices.length > 0) { + constraints.push({ + "type": "PathContainsVerticesAtMostOnce", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + "vertices": this.atMostOnceVertices.map(value => value + 1) + }); + } + //--------------------- Edges --------------------- + if(this.exactlyOnceEdges.length > 0) { + constraints.push({ + "type": "PathContainsEdgesExactlyOnce", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + "edges": this.exactlyOnceEdges.map(pair => pair.map((val) => parseInt(val))) + }); + } + if(this.atLeastOnceEdges.length > 0) { + constraints.push({ + "type": "PathContainsEdgesAtLeastOnce", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + "edges": this.atLeastOnceEdges.map(pair => pair.map((val) => parseInt(val))) + }); + } + if(this.atMostOnceEdges.length > 0) { + constraints.push({ + "type": "PathContainsEdgesAtMostOnce", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + "edges": this.atMostOnceEdges.map(pair => pair.map((val) => parseInt(val))) + }); + } + if(this.pathPositionIs.length > 0) { + const keyValue: [string[], string][] = this.pathPositionIs.map(value => [[value[0], value[1]], value[2]]); + const map = new Map(); + for(const entry of keyValue) { + if(!map.has(JSON.stringify(entry[0]))) { + map.set(JSON.stringify(entry[0]), []); + } + map.get(JSON.stringify(entry[0]))!.push(entry[1]); + } + console.log(map.keys()) - if (this.shareNoEdges.length > 0) { - constraints.push({ - type: "PathsShareNoEdges", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - }); - } - if (this.shareNoVertices.length > 0) { - constraints.push({ - type: "PathsShareNoVertices", - path_ids: Array(this.nPaths) - .fill(0) - .map((_, index) => index + 1), - }); - } + for(const entry of map.keys()) { + const entry_array = JSON.parse(entry); + constraints.push({ + "type": "PathPositionIs", + "position": parseInt(entry_array[1]), + "vertices": map.get(entry)!.map(vertex => parseInt(vertex)), + "path_id": parseInt(entry_array[0]) + }); - object["constraints"] = constraints; - return JSON.stringify(object); - } + } + } + if(this.precedences.length > 0) { + constraints.push({ + "type": "PrecedenceConstraint", + "precedences": this.precedences.map(value => ({ + "before": parseInt(value[0]), + "after": parseInt(value[1]) + })) + }); + } + + if(this.shareNoEdges.length > 0) { + constraints.push({ + "type": "PathsShareNoEdges", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + }); + } + if(this.shareNoVertices.length > 0) { + constraints.push({ + "type": "PathsShareNoVertices", + "path_ids": Array(this.nPaths).fill(0).map((_, index) => index + 1), + }); + } + + object["constraints"] = constraints; + return JSON.stringify(object); + } } -export default Settings; +export default Settings diff --git a/webpage/app/titledTextbox.tsx b/webpage/app/titledTextbox.tsx index ceebac1e..f0e774d9 100644 --- a/webpage/app/titledTextbox.tsx +++ b/webpage/app/titledTextbox.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React from 'react'; type TitledTextboxProps = { title: string; @@ -6,20 +6,11 @@ type TitledTextboxProps = { onChange?: (value: string) => void; }; -const TitledTextbox: React.FC = ({ - title, - defaultValue, - onChange, -}) => { +const TitledTextbox: React.FC = ({ title, defaultValue, onChange }) => { return (
- onChange?.(e.currentTarget.value!)} - /> + onChange?.(e.currentTarget.value!)} />
); }; diff --git a/webpage/app/toggle.tsx b/webpage/app/toggle.tsx index 4128de8b..05e49e87 100644 --- a/webpage/app/toggle.tsx +++ b/webpage/app/toggle.tsx @@ -1,33 +1,30 @@ -import React, { useState } from "react"; -import styles from "./style.module.css"; +import React, { useState } from 'react'; +import styles from './style.module.css' interface ToggleProps { - children: React.ReactNode; - state?: boolean; - onUpdate?: (isToggled: boolean) => void; + children: React.ReactNode; + state?: boolean; + onUpdate?: (isToggled: boolean) => void; } const Toggle: React.FC = ({ children, state, onUpdate }) => { - const [isToggled, setIsToggled] = useState(false); + const [isToggled, setIsToggled] = useState(false); - const getIsToggled = () => { - return state ?? isToggled; - }; + const getIsToggled = () => { + return state ?? isToggled; + } - const handleToggle = () => { - const newState = !getIsToggled(); - setIsToggled(newState); - onUpdate?.(newState); - }; + const handleToggle = () => { + const newState = !getIsToggled(); + setIsToggled(newState); + onUpdate?.(newState); + }; - return ( - - ); + return ( + + ); }; export default Toggle; diff --git a/webpage/app/toggleBag.tsx b/webpage/app/toggleBag.tsx index d59f0149..431b035d 100644 --- a/webpage/app/toggleBag.tsx +++ b/webpage/app/toggleBag.tsx @@ -1,71 +1,60 @@ -import React, { useState } from "react"; -import Toggle from "./toggle"; -import { useEffect } from "react"; +import React, { useState } from 'react'; +import Toggle from './toggle'; +import { useEffect } from 'react'; interface ToggleBagProps { - title: string; - items: string[]; - all: boolean; - cols?: number; - mutualExclusions?: number[][]; - onChange?: (states: boolean[]) => void; + title: string; + items: string[]; + all: boolean; + cols?: number; + mutualExclusions?: number[][]; + onChange?: (states: boolean[]) => void; } -const ToggleBag: React.FC = ({ - title, - items, - all, - cols = 4, - mutualExclusions = [], - onChange, -}) => { - // Component logic goes here - const [states, setStates] = useState( - Array(items.length + (all ? 1 : 0)).fill(false), - ); - useEffect(() => { - if (states.length !== items.length + (all ? 1 : 0)) { - setStates(Array(items.length + (all ? 1 : 0)).fill(false)); - } - }, [items]); +const ToggleBag: React.FC = ({ title, items, all, cols = 4, mutualExclusions = [], onChange }) => { + // Component logic goes here + const [states, setStates] = useState(Array(items.length + (all ? 1 : 0)).fill(false)); + useEffect(() => { + if (states.length !== items.length + (all ? 1 : 0)) { + setStates(Array(items.length + (all ? 1 : 0)).fill(false)); + } + }, [items]); - const update = (index: number, state: boolean) => { - const newStates = [...states]; - for (const exclusion of mutualExclusions) { - if (exclusion.includes(index)) { - for (const i of exclusion) { - newStates[i + (all ? 1 : 0)] = false; + const update = (index: number, state: boolean) => { + const newStates = [...states]; + for(const exclusion of mutualExclusions) { + if(exclusion.includes(index)) { + for(const i of exclusion) { + newStates[i + (all ? 1 : 0)] = false; + } + } } - } + newStates[index + (all ? 1 : 0)] = state; + if(index == -1 && states.every((state) => state)) + newStates.fill(false); + else if(index == -1) + newStates.fill(true); + else if(all && newStates.every((state, index) => index == 0 || state)) + newStates[0] = true; + else if(all && newStates.every((state, index) => index == 0 || !state)) + newStates[0] = false; + setStates(newStates); + onChange?.(newStates); } - newStates[index + (all ? 1 : 0)] = state; - if (index == -1 && states.every((state) => state)) newStates.fill(false); - else if (index == -1) newStates.fill(true); - else if (all && newStates.every((state, index) => index == 0 || state)) - newStates[0] = true; - else if (all && newStates.every((state, index) => index == 0 || !state)) - newStates[0] = false; - setStates(newStates); - onChange?.(newStates); - }; - return ( - // JSX markup goes here -
-

{title}

-
- {(all ? ["ALL"].concat(items) : items).map((item, index) => ( - update(index - (all ? 1 : 0), state)} - key={index} - > - {item} - - ))} -
-
- ); + return ( + // JSX markup goes here +
+

{title}

+
+ { + (all ? (["ALL"].concat(items)) : items).map((item, index) => ( + update(index - (all ? 1 : 0), state)} key={index}>{item} + )) + } +
+
+ ); }; export default ToggleBag; diff --git a/webpage/types/react-graph.vis.d.ts b/webpage/types/react-graph.vis.d.ts index c2163833..c471f0e5 100644 --- a/webpage/types/react-graph.vis.d.ts +++ b/webpage/types/react-graph.vis.d.ts @@ -8,6 +8,6 @@ * Licensed under the MIT License */ -declare module "react-graph-vis" { - export default class Graph extends React.Component {} +declare module 'react-graph-vis' { + export default class Graph extends React.Component {}; } From fb2f5b06ca586ab02c82cd76706dadd25665e30b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 2 Feb 2026 13:04:28 +0100 Subject: [PATCH 9/9] Update .pre-commit-config.yaml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lukas Burgholzer --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8b19d431..062e1f2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,7 @@ repos: name: Disallow improper capitalization language: pygrep entry: '\b(?:Numpy|Github|PyTest|Mqt|Tum)\b' - exclude: ^(.pre-commit-config.yaml|webpage/package-lock.json|.*.zip) + exclude: ^(.pre-commit-config.yaml|webpage/package-lock.json|.*\.zip) priority: 0 ## Check for spelling