master > master: code py - models + config implementiert

This commit is contained in:
RD 2022-06-10 11:50:59 +02:00
parent 67f6caf2d5
commit 0523c68100
21 changed files with 538 additions and 94 deletions

View File

@ -22,12 +22,16 @@
!/src/**/*.py
!/main.py
!/models
!/models/*-schema.yaml
!/models/README.md
!/tests
!/tests/**/
!/tests/**/*.py
!/assets
!/assets/**/
!/assets/*.yaml
!/dist
!/dist/VERSION

View File

@ -12,7 +12,7 @@ die Methoden mit Daten ausprobieren.
1. Der Python-Compiler **`^3.10.*`** wird benötigt.
2. Es ist auch empfehlenswert, **`justfile`** zu installieren (siehe <https://github.com/casey/just#installation>).
## Setup -> Test -> Run ##
## Build -> Test -> Run ##
In einem IDE in dem Repo zu diesem Ordner navigieren.
</br>
@ -23,7 +23,7 @@ Wer das **justfile**-Tool hat:
# Zeige alle Befehle:
just
# Zur Installation der Requirements (nur nach Änderungen):
just setup;
just build;
# Zur Ausführung der unit tests:
just tests;
# Zur Ausführung des Programms
@ -43,3 +43,9 @@ python3 main.py
Auf Windows verwendet man `py -3` od. `py -310` statt `python3`.
Man kann auch mit einem guten Editor/IDE die Tests einzeln ausführen.
## Testfälle durch Config-Datei ##
Statt den Code immer anfassen zu müssen, kann man Fälle für die verschiedenen Algorithmen
in der **[./assets/commands](assets/commands.yaml)** erfassen und (mittels `just run`)
das Programm ausführen.

View File

@ -0,0 +1,33 @@
## Beispiel für Seminarwoche 9 (Blatt 8)
- name: TSP
dist:
- [0, 7, 4, 3]
- [7, 0, 5, 6]
- [2, 5, 0, 5]
- [2, 7, 4, 0]
optimise: MIN
verbose: true
## Beispiele für Seminarwoche 10 (Blatt 9)
- &command_hirschberg
name: HIRSCHBERG
once: true
verbose:
- COSTS
- MOVES
# show:
# # - ATOMS
# - TREE
word1: 'happily ever after'
word2: 'apples'
- <<: *command_hirschberg
word1: 'ANSTRENGEN'
word2: 'ANSPANNEN'
- <<: *command_hirschberg
word1: 'ACGAAG'
word2: 'AGAT'
- <<: *command_hirschberg
word1: 'happily ever, lol'
word2: 'apple'
- <<: *command_hirschberg
word1: 'happily'
word2: 'applses'

View File

@ -0,0 +1,8 @@
info:
author: Raj Dahya
title: Algorithmen und Datenstrukturen 2
description: |-
Ein Code-Projekt, das Algorithmen und Datenstrukturen aus dem Kurs
ADS2 an der Universität Leipzig (Sommersemester 2022)
implementiert.
options: {}

View File

@ -1 +1 @@
0.0.0
0.1.0

View File

@ -11,6 +11,7 @@ _default:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PYTHON := if os_family() == "windows" { "py -3" } else { "python3" }
GEN_MODELS := "datamodel-codegen"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Macros
@ -42,6 +43,19 @@ _docker-build-and-log service:
_docker-build-and-interact service container:
@docker compose up --build -d {{service}} && docker attach {{container}}
_generate-models path name:
@{{GEN_MODELS}} \
--input-file-type openapi \
--encoding "UTF-8" \
--disable-timestamp \
--use-schema-description \
--allow-population-by-field-name \
--snake-case-field \
--strict-nullable \
--target-python-version 3.9 \
--input {{path}}/{{name}}-schema.yaml \
--output {{path}}/generated/{{name}}.py
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TARGETS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -50,8 +64,16 @@ _docker-build-and-interact service container:
# TARGETS: build
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
build:
build: _build-requirements _build-skip-requirements
_build-skip-requirements: build-models
_build-requirements:
@{{PYTHON}} -m pip install --disable-pip-version-check -r requirements.txt
build-models: _check-system-requirements _build-models-nochecks
_build-models-nochecks:
@echo "Generate data models from schemata."
@just _create-folder-if-not-exists "models/generated"
@- just _generate-models "models" "config"
@- just _generate-models "models" "commands"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TARGETS: run
@ -145,3 +167,8 @@ _display-logs:
check-system:
@echo "Operating System detected: {{os_family()}}."
@echo "Python command used: {{PYTHON}}."
_check-system-requirements:
@if ! ( {{GEN_MODELS}} --help >> /dev/null 2> /dev/null ); then \
echo "Command '{{GEN_MODELS}}' did not work. Ensure that the installation of 'datamodel-code-generator' worked and that system paths are set." \
exit 1; \
fi

View File

@ -6,18 +6,23 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import os;
import sys;
import sys
from token import MINUS;
os.chdir(os.path.join(os.path.dirname(__file__), '..'));
os.chdir(os.path.join(os.path.dirname(__file__)));
sys.path.insert(0, os.getcwd());
from src.core.log import *;
from src.thirdparty.maths import *;
from models.generated.commands import *;
from src.core.log import *;
from src.setup.config import *;
from src.graphs.graph import *;
from src.graphs.tarjan import *;
from src.travel.naive import *;
from src.tsp import *;
from src.hirschberg import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL CONSTANTS/VARIABLES
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -29,38 +34,21 @@ from src.hirschberg import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def enter():
# ## Beispiel für Seminarwoche 9 (Blatt 8):
# tsp_naive_algorithm(
# dist = np.asarray([
# [0, 7, 4, 3],
# [7, 0, 5, 6],
# [2, 5, 0, 5],
# [2, 7, 4, 0],
# ], dtype=float),
# optimise=min,
# verbose=True,
# );
## Beispiel für Seminarwoche 10 (Blatt 9):
hirschberg_algorithm(
# Y = 'ANSPANNEN',
# X = 'ANSTRENGEN',
# Y = 'AGAT',
# X = 'ACGAAG',
Y = 'apples',
X = 'happily ever after',
# Y = 'applses',
# X = 'happily ever, lol',
# Y = 'apple',
# X = 'happily',
# once = True,
# verb = VerboseMode.COSTS,
# verb = VerboseMode.MOVES,
verb = VerboseMode.COSTS_AND_MOVES,
show = [
# DisplayOptions.ATOMS,
DisplayOptions.TREE,
],
);
for command in COMMANDS:
if isinstance(command, CommandTsp):
tsp_algorithm(
dist = np.asarray(command.dist, dtype=float),
optimise = min if command.optimise == EnumTspOptimise.min else max,
verbose = command.verbose,
);
elif isinstance(command, CommandHirschberg):
hirschberg_algorithm(
X = command.word1,
Y = command.word2,
once = command.once,
verb = command.verbose,
show = command.show,
);
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,51 @@
# Models #
- In this folder the configuration files for the models of data classes (as `*.yaml`-files) are stored.
- These are interpreted by `openapi` to generate the classes for the source code during the `build` phase of the programme.
- Once the models are generated, the app configurations are read at run time and interpreted as these models.
- Generating instead of manually encoding the classes is safer/more stable as it allows for validation.
## Folder structure ##
As per the 3rd point, the `yaml`-file/s for the models (schemata)
and the `yaml`-file/s for actual configuration values for the app
are to be kept separately.
All models are to be stored in [./models](../models/),
whereas configuration files are to be stored in [./assets](../assets/).
Note that the generated python files are not stored in the repository.
When deployed on a server, these are generated as part of the `build`-process.
## Developer notes ##
### Prerequisites ###
For the python source code, we currently use:
- Python: `v3.10.*`
- Modules:
- `datamodel-code-generator==0.12.0`
(This is all taken care of during the `build` process.)
### Building the models ###
To build the models before run time, use the following command + options:
```bash
datamodel-codegen
--input-file-type openapi
--encoding "UTF-8"
--disable-timestamp
--use-schema-description
--snake-case-field
--strict-nullable
--input <path/to/file.yml>
--output <path/to/file.py>
```
(cf. https://pydantic-docs.helpmanual.io/datamodel_code_generator/).
Alternatively, call:
```
just build
```

View File

@ -0,0 +1,140 @@
openapi: 3.0.3
info:
version: 0.1.0
title: Schemata for command instructions
servers: []
paths: {}
components:
schemas:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Commands
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Commands:
description: |-
List of commands to test algorithms/datastructures.
type: array
items:
$ref: "#/components/schemas/Command"
default: []
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Command
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Command:
description: |-
Instructions for command to call
required:
- name
properties: &ref_command_properties
name:
$ref: '#/components/schemas/EnumAlgorithmNames'
additionalProperties: true
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Command - Algorithm: Tarjan
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CommandTarjan:
description: |-
Instructions for execution of Tarjan-Algorithm
type: object
required:
- name
properties:
<<: *ref_command_properties
# required:
# properties:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Command - Algorithm: TSP
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CommandTsp:
description: |-
Instructions for execution of TSP-Algorithm
type: object
required:
- name
- optimise
- dist
properties:
<<: *ref_command_properties
dist:
type: array
items:
type: array
items:
type: number
optimise:
$ref: '#/components/schemas/EnumTSPOptimise'
verbose:
type: boolean
default: false
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Command - Algorithm: Hirschberg
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CommandHirschberg:
description: |-
Instructions for execution of Hirschberg-Algorithm
type: object
required:
- name
- horizontal
- vertical
properties:
<<: *ref_command_properties
word1:
description: Word that gets placed vertically in algorithm.
type: string
word2:
description: Word that gets placed horizontally in algorithm
type: string
once:
type: boolean
default: false
verbose:
type: array
items:
$ref: '#/components/schemas/EnumHirschbergVerbosity'
default: []
show:
type: array
items:
$ref: '#/components/schemas/EnumHirschbergShow'
default: []
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Enum Algorithm Names
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EnumAlgorithmNames:
description: |-
Enumeration of possible algorithm options.
type: string
enum:
- TARJAN
- TSP
- HIRSCHBERG
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Enum TSP - Optimise Mode
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EnumTSPOptimise:
description: |-
Enumeration of optimisation options for TSP
type: string
enum:
- MIN
- MAX
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Enum Hirschberg - Verbosity options
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EnumHirschbergVerbosity:
description: |-
Enumeration of verbosity options for Hirschberg
type: string
enum:
- COSTS
- MOVES
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Enum Hirschberg - display options
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EnumHirschbergShow:
description: |-
Enumeration of verbosity options for Hirschberg
type: string
enum:
- TREE
- ATOMS

View File

@ -0,0 +1,49 @@
openapi: 3.0.3
info:
version: 0.1.0
title: Schemata for config models
servers: []
paths: {}
components:
schemas:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Config
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Config:
descripton: |-
Data model for all parts of the configuration.
type: object
required:
- info
- options
- calls
properties:
info:
$ref: "#/components/schemas/Info"
options:
$ref: "#/components/schemas/AppOptions"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Info
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Info:
description: |-
Contains meta data about project.
type: object
required:
- title
- description
- author
properties:
title:
type: string
description:
type: string
author:
type: string
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# App Options
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AppOptions:
description: |-
Options pertaining to the rudimentary setup of the app.
type: object

View File

@ -1,7 +1,35 @@
pip>=22.0.4
pip>=22.1.2
wheel>=0.37.1
# running
anyio>=3.5.0
aiohttp>=3.8.1
asyncio>=3.4.3
codetiming>=1.3.0
# testing + dev
coverage[toml]>=6.4
pytest>=7.1.1
pytest-asyncio>=0.18.3
pytest-cov>=3.0.0
pytest-lazy-fixture>=0.6.3
pytest-order>=1.0.1
testfixtures>=6.18.5
# config
python-dotenv>=0.2.0
jsonschema>=4.4.0
lazy-load>=0.8.2
pyyaml>=6.0
pydantic>=1.9.0
datamodel-code-generator>=0.12.0
# misc
lorem>=0.1.1
safetywrap>=1.5.0
typing>=3.7.4.3
# maths
numpy>=1.22.3
pandas>=1.4.1
tabulate>=0.8.9

View File

@ -15,6 +15,4 @@ from src.hirschberg.display import *;
__all__ = [
'hirschberg_algorithm',
'VerboseMode',
'DisplayOptions',
];

View File

@ -8,6 +8,7 @@
from src.thirdparty.types import *;
from src.thirdparty.maths import *;
from models.generated.commands import *;
from src.hirschberg.constants import *;
from src.hirschberg.display import *;
from src.hirschberg.matrix import *;
@ -30,8 +31,8 @@ __all__ = [
def simple_algorithm(
X: str,
Y: str,
verb: VerboseMode = VerboseMode.NONE,
show: List[DisplayOptions] = [],
verb: List[EnumHirschbergVerbosity] = [],
show: List[EnumHirschbergShow] = [],
) -> Tuple[str, str]:
'''
Dieser Algorithmus berechnet die Edit-Distanzen + optimale Richtungen ein Mal.
@ -40,7 +41,7 @@ def simple_algorithm(
Costs, Moves = compute_cost_matrix(X = '-' + X, Y = '-' + Y);
path = reconstruct_optimal_path(Moves=Moves);
word_x, word_y = reconstruct_words(X = '-' + X, Y = '-' + Y, moves=[Moves[coord] for coord in path], path=path);
if verb != VerboseMode.NONE:
if verb != []:
repr = display_cost_matrix(Costs=Costs, path=path, X = '-' + X, Y = '-' + Y, verb=verb);
display = word_y + f'\n{"-"*len(word_x)}\n' + word_x;
print(f'\n{repr}\n\n\x1b[1mOptimales Alignment:\x1b[0m\n\n{display}\n');
@ -50,8 +51,8 @@ def hirschberg_algorithm(
X: str,
Y: str,
once: bool = False,
verb: VerboseMode = VerboseMode.NONE,
show: List[DisplayOptions] = [],
verb: List[EnumHirschbergVerbosity] = [],
show: List[EnumHirschbergShow] = [],
) -> Tuple[str, str]:
'''
Der Hirschberg-Algorithmus berechnet nur die Edit-Distanzen (Kostenmatrix)
@ -72,8 +73,8 @@ def hirschberg_algorithm(
word_y = align.as_string2();
# verbose output hier behandeln (irrelevant für Algorithmus):
if verb != VerboseMode.NONE:
if DisplayOptions.TREE in show:
if verb != []:
if EnumHirschbergShow.tree in show:
display = align.astree(braces=True);
else:
display_x = align.as_string1(braces=True);
@ -87,8 +88,8 @@ def hirschberg_algorithm_step(
X: str,
Y: str,
depth: int = 0,
verb: VerboseMode = VerboseMode.NONE,
show: List[DisplayOptions] = [],
verb: List[EnumHirschbergVerbosity] = [],
show: List[EnumHirschbergShow] = [],
) -> Alignment:
'''
Der rekursive Schritt der Hirschberg-Algorithmus teil eines der Wörter in zwei
@ -105,7 +106,7 @@ def hirschberg_algorithm_step(
word_x, word_y = reconstruct_words(X = '-' + X, Y = '-' + Y, moves=[Moves[coord] for coord in path], path=path);
# verbose output hier behandeln (irrelevant für Algorithmus):
if verb != VerboseMode.NONE and (DisplayOptions.ATOMS in show):
if verb != [] and (EnumHirschbergShow.atoms in show):
repr = display_cost_matrix(Costs=Costs, path=path, X = '-' + X, Y = '-' + Y, verb=verb);
print(f'\n\x1b[1mRekursionstiefe: {depth}\x1b[0m\n\n{repr}')
@ -126,7 +127,7 @@ def hirschberg_algorithm_step(
Costs2, Moves2 = compute_cost_matrix(X = '-' + X2, Y = '-' + Y2);
# verbose output hier behandeln (irrelevant für Algorithmus):
if verb != VerboseMode.NONE:
if verb != []:
path1, path2 = reconstruct_optimal_path_halves(Costs1=Costs1, Costs2=Costs2, Moves1=Moves1, Moves2=Moves2);
repr = display_cost_matrix_halves(
Costs1 = Costs1,

View File

@ -12,8 +12,6 @@ from src.thirdparty.types import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'VerboseMode',
'DisplayOptions',
'Directions',
'gap_penalty',
'missmatch_penalty',
@ -23,16 +21,6 @@ __all__ = [
# ENUMS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class VerboseMode(Enum):
NONE = -1;
COSTS = 0;
MOVES = 1;
COSTS_AND_MOVES = 2;
class DisplayOptions(Enum):
TREE = 0;
ATOMS = 1;
class Directions(Enum):
UNSET = -1;
# Prioritäten hier setzen

View File

@ -8,6 +8,7 @@
from src.thirdparty.types import *;
from src.thirdparty.maths import *;
from models.generated.commands import *;
from src.hirschberg.constants import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -29,7 +30,7 @@ def represent_cost_matrix(
path: List[Tuple[int, int]],
X: str,
Y: str,
verb: VerboseMode,
verb: List[EnumHirschbergVerbosity],
pad: bool = False,
) -> np.ndarray: # NDArray[(Any, Any), Any]:
m = len(X); # display vertically
@ -42,28 +43,27 @@ def represent_cost_matrix(
table = np.full(shape=(3 + m, 3 + n), dtype=object, fill_value='');
# topmost rows:
table[0, 3:(3+n)] = [str(j) for j in range(n)];
table[1, 3:(3+n)] = [y for y in Y];
table[0, 3:(3+n)] = [ f'\x1b[2m{j}\x1b[0m' for j in range(n) ];
table[1, 3:(3+n)] = [ f'\x1b[1m{y}\x1b[0m' for y in Y ];
table[2, 3:(3+n)] = '--';
# leftmost columns:
table[3:(3+m), 0] = [str(i) for i in range(m)];
table[3:(3+m), 1] = [x for x in X];
table[3:(3+m), 0] = [ f'\x1b[2m{i}\x1b[0m' for i in range(m) ];
table[3:(3+m), 1] = [ f'\x1b[1m{x}\x1b[0m' for x in X ];
table[3:(3+m), 2] = '|';
if pad:
table[-3, 3:(3+n)] = '--';
table[3:(3+m), -1] = '|';
match verb:
case VerboseMode.MOVES:
table[3:(3+m), 3:(3+n)] = '\x1b[2m.\x1b[0m';
if EnumHirschbergVerbosity.costs in verb:
table[3:(3+m), 3:(3+n)] = Costs.copy();
if EnumHirschbergVerbosity.moves in verb:
for (i, j) in path:
table[3 + i, 3 + j] = '\x1b[31;1m*\x1b[0m';
case VerboseMode.COSTS | VerboseMode.COSTS_AND_MOVES:
table[3:(3+m), 3:(3+n)] = Costs.copy();
if verb == VerboseMode.COSTS_AND_MOVES:
for (i, j) in path:
table[3 + i, 3 + j] = f'\x1b[31;4;1m{table[3 + i, 3 + j]}\x1b[0m';
table[3 + i, 3 + j] = f'\x1b[31;4;1m{table[3 + i, 3 + j]}\x1b[0m';
elif EnumHirschbergVerbosity.moves in verb:
table[3:(3+m), 3:(3+n)] = '\x1b[2m.\x1b[0m';
for (i, j) in path:
table[3 + i, 3 + j] = '\x1b[31;1m*\x1b[0m';
return table;
@ -72,7 +72,7 @@ def display_cost_matrix(
path: List[Tuple[int, int]],
X: str,
Y: str,
verb: VerboseMode,
verb: EnumHirschbergVerbosity,
) -> str:
'''
Zeigt Kostenmatrix + optimalen Pfad.
@ -99,7 +99,7 @@ def display_cost_matrix_halves(
X2: str,
Y1: str,
Y2: str,
verb: VerboseMode,
verb: EnumHirschbergVerbosity,
) -> str:
'''
Zeigt Kostenmatrix + optimalen Pfad für Schritt im D & C Hirschberg-Algorithmus

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.thirdparty.misc import *;
from src.thirdparty.config import *;
from src.thirdparty.code import *;
from src.thirdparty.types import *;
from models.generated.config import *;
from models.generated.commands import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'INFO',
'OPTIONS',
'COMMANDS',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PATH_ASSETS_CONFIG: str = 'assets/config.yaml';
PATH_ASSETS_COMMANDS: str = 'assets/commands.yaml';
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# LAZY LOADED RESOURCES
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def load_assets_config(path: str) -> Config: # pragma: no cover
with open(path, 'r') as fp:
assets = yaml_load(fp, Loader=yaml_FullLoader);
assert isinstance(assets, dict);
return Config(**assets);
def create_commands(path: str) -> List[Command]: # pragma: no cover
commands = [];
with open(path, 'r') as fp:
assets = yaml_load(fp, Loader=yaml_FullLoader);
for command in assets:
match Command(**command).name:
case EnumAlgorithmNames.tarjan:
commands.append(CommandTarjan(**command));
case EnumAlgorithmNames.tsp:
commands.append(CommandTsp(**command));
case EnumAlgorithmNames.hirschberg:
commands.append(CommandHirschberg(**command));
return commands;
# use lazy loaing to ensure that values only loaded (once) when used
CONFIG: Config = lazy(load_assets_config, path=PATH_ASSETS_CONFIG);
INFO: Info = lazy(lambda x: x.info, CONFIG);
OPTIONS: AppOptions = lazy(lambda x: x.options, CONFIG);
COMMANDS: List[Command] = lazy(create_commands, path=PATH_ASSETS_COMMANDS);

43
code/python/src/thirdparty/code.py vendored Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from functools import wraps;
from functools import partial;
from dataclasses import dataclass;
from dataclasses import field;
from dataclasses import Field;
from dataclasses import asdict;
from dataclasses import MISSING;
from itertools import product as itertools_product;
# cf. https://github.com/mplanchard/safetywrap
from safetywrap import Ok;
from safetywrap import Err;
from safetywrap import Nothing;
from safetywrap import Result;
from safetywrap import Option;
from safetywrap import Some;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'partial',
'wraps',
'asdict',
'dataclass',
'field',
'Field',
'MISSING',
'itertools_product',
'Err',
'Nothing',
'Ok',
'Option',
'Result',
'Some',
];

View File

@ -6,10 +6,12 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import json;
import jsonschema;
from lazy_load import lazy;
from yaml import add_constructor;
from yaml import load;
from yaml import Loader;
from yaml import FullLoader;
from yaml import load as yaml_load;
from yaml import FullLoader as yaml_FullLoader;
from yaml import add_path_resolver as yaml_add_path_resolver;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
@ -17,8 +19,10 @@ from yaml import FullLoader;
__all__ = [
'json',
'jsonschema',
'lazy',
'add_constructor',
'load',
'Loader',
'FullLoader',
'yaml_load',
'yaml_FullLoader',
'yaml_add_path_resolver',
];

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.tsp.algorithms import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'tsp_algorithm',
];

View File

@ -5,7 +5,6 @@
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations;
from src.thirdparty.types import *;
from src.thirdparty.maths import *;
@ -14,15 +13,15 @@ from src.thirdparty.maths import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'tsp_naive_algorithm',
'tsp_algorithm',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHOD tsp_naive_algorithm
# METHOD tsp_algorithm
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def tsp_naive_algorithm(
dist: NDArray[(Any, Any), float],
def tsp_algorithm(
dist: np.ndarray, # NDArray[(Any, Any), float],
optimise = min,
verbose: bool = False,
) -> tuple[float, list[list[int]]]: