diff --git a/code/python/assets/commands.yaml b/code/python/assets/commands.yaml index 791ff66..2de94d8 100644 --- a/code/python/assets/commands.yaml +++ b/code/python/assets/commands.yaml @@ -89,10 +89,8 @@ algorithm: BRANCH-AND-BOUND max-cost: 10 items: [a, b, c, d, e] - costs: - [3, 4, 5, 2, 1] - values: - [8, 7, 8, 3, 2] + costs: [3, 4, 5, 2, 1] + values: [8, 7, 8, 3, 2] - name: RUCKSACK algorithm: BRANCH-AND-BOUND max-cost: 460 @@ -103,10 +101,8 @@ 'Schokolade', 'Apfelringe', ] - costs: - [220, 80, 140, 90, 100] - values: - [100, 10, 70, 80, 100] + costs: [220, 80, 140, 90, 100] + values: [100, 10, 70, 80, 100] - name: RUCKSACK algorithm: BRANCH-AND-BOUND max-cost: 90 @@ -117,7 +113,17 @@ 'Hirse', 'Sellerie', ] - costs: - [30, 10, 50, 10, 80] - values: - [17, 14, 17, 5, 25] + costs: [30, 10, 50, 10, 80] + values: [17, 14, 17, 5, 25] + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Beispiele für Seminarwoche 12 +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- name: EUKLID + numbers: + - 2017 + - 58 +- name: POLLARD-RHO + number: 534767 + x-init: 5 diff --git a/code/python/assets/config.yaml b/code/python/assets/config.yaml index a18cf06..5842dfa 100644 --- a/code/python/assets/config.yaml +++ b/code/python/assets/config.yaml @@ -26,13 +26,21 @@ options: verbose: - COSTS - MOVES - # show: [] - show: - # - ATOMS - - TREE + show: [] + # show: + # - ATOMS + # - TREE rucksack: - verbose: true - # show: [] - show: - - ALL-WEIGHTS - - ALL-SUMS + verbose: *ref_verbose + show: [] + # show: + # - ALL-WEIGHTS + # - ALL-SUMS + genetic: + verbose: *ref_verbose + random-walk: + verbose: *ref_verbose + euklid: + verbose: *ref_verbose + pollard-rho: + verbose: *ref_verbose diff --git a/code/python/dist/VERSION b/code/python/dist/VERSION index 0ea3a94..0d91a54 100644 --- a/code/python/dist/VERSION +++ b/code/python/dist/VERSION @@ -1 +1 @@ -0.2.0 +0.3.0 diff --git a/code/python/docs/commands/Models/CommandEuklid.md b/code/python/docs/commands/Models/CommandEuklid.md new file mode 100644 index 0000000..71a8852 --- /dev/null +++ b/code/python/docs/commands/Models/CommandEuklid.md @@ -0,0 +1,10 @@ +# CommandEuklid +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**name** | [**EnumAlgorithmNames**](EnumAlgorithmNames.md) | | [default to null] +**numbers** | [**List**](integer.md) | | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/code/python/docs/commands/Models/CommandPollard.md b/code/python/docs/commands/Models/CommandPollard.md new file mode 100644 index 0000000..8541020 --- /dev/null +++ b/code/python/docs/commands/Models/CommandPollard.md @@ -0,0 +1,11 @@ +# CommandPollard +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**name** | [**EnumAlgorithmNames**](EnumAlgorithmNames.md) | | [default to null] +**number** | [**Integer**](integer.md) | | [default to null] +**xMinusinit** | [**Integer**](integer.md) | | [optional] [default to 2] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/code/python/docs/commands/Models/CommandRucksack.md b/code/python/docs/commands/Models/CommandRucksack.md index bae4f88..f61d4d6 100644 --- a/code/python/docs/commands/Models/CommandRucksack.md +++ b/code/python/docs/commands/Models/CommandRucksack.md @@ -3,6 +3,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**name** | [**EnumAlgorithmNames**](EnumAlgorithmNames.md) | | [default to null] **algorithm** | [**EnumRucksackAlgorithm**](EnumRucksackAlgorithm.md) | | [default to null] **allowMinusfractional** | [**Boolean**](boolean.md) | | [optional] [default to false] **maxMinuscost** | [**BigDecimal**](number.md) | Upper bound for total cost of rucksack. | [default to null] diff --git a/code/python/docs/commands/README.md b/code/python/docs/commands/README.md index d9004fd..54e7d2e 100644 --- a/code/python/docs/commands/README.md +++ b/code/python/docs/commands/README.md @@ -13,7 +13,9 @@ Class | Method | HTTP request | Description ## Documentation for Models - [Command](.//Models/Command.md) + - [CommandEuklid](.//Models/CommandEuklid.md) - [CommandHirschberg](.//Models/CommandHirschberg.md) + - [CommandPollard](.//Models/CommandPollard.md) - [CommandRucksack](.//Models/CommandRucksack.md) - [CommandTarjan](.//Models/CommandTarjan.md) - [CommandTsp](.//Models/CommandTsp.md) diff --git a/code/python/docs/config/Models/AppOptions.md b/code/python/docs/config/Models/AppOptions.md index b100ef5..c2b9d20 100644 --- a/code/python/docs/config/Models/AppOptions.md +++ b/code/python/docs/config/Models/AppOptions.md @@ -9,6 +9,8 @@ Name | Type | Description | Notes **tsp** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null] **hirschberg** | [**AppOptions_hirschberg**](AppOptions_hirschberg.md) | | [default to null] **rucksack** | [**AppOptions_rucksack**](AppOptions_rucksack.md) | | [default to null] +**euklid** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null] +**pollardMinusrho** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/code/python/models/commands-schema.yaml b/code/python/models/commands-schema.yaml index 4b76e19..753eac2 100644 --- a/code/python/models/commands-schema.yaml +++ b/code/python/models/commands-schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 0.2.0 + version: 0.3.0 title: Schemata for command instructions servers: - url: "." @@ -115,11 +115,14 @@ components: Instructions for execution of Branch & Bound-Algorithm for the Rucksack-Problem type: object required: + - name - algorithm - max-cost - costs - values properties: + name: + $ref: '#/components/schemas/EnumAlgorithmNames' algorithm: $ref: '#/components/schemas/EnumRucksackAlgorithm' allow-fractional: @@ -146,6 +149,147 @@ components: items: type: string default: [] + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Algorithm: Random Walk + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CommandRandomWalk: + description: |- + Instructions for execution of random walks to determine local extrema in a fitness landscape + type: object + required: + - name + - algorithm + - landscape + - optimise + properties: + name: + $ref: '#/components/schemas/EnumAlgorithmNames' + algorithm: + $ref: '#/components/schemas/EnumWalkMode' + landscape: + $ref: '#/components/schemas/DataTypeLandscapeGeometry' + optimise: + $ref: '#/components/schemas/EnumOptimiseMode' + coords-init: + description: Initial co-ordinates to start the algorithm. + type: array + items: + type: integer + minItems: 1 + temperature-init: + type: float + default: 1. + annealing: + type: boolean + default: false + one-based: + type: boolean + default: false + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Algorithm: Genetic Algorithm + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CommandGenetic: + description: |- + Instructions for execution of the Genetic algorithm + type: object + required: + - name + - population + properties: + name: + $ref: '#/components/schemas/EnumAlgorithmNames' + population: + type: array + items: + type: array + items: + type: string + minItems: 2 + # maxItems: 2 # FIXME: does not work! + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Algorithm: Euklidean algorithm + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CommandEuklid: + description: |- + Instructions for execution of the Euklidean gcd-algorithm + type: object + required: + - name + - numbers + properties: + name: + $ref: '#/components/schemas/EnumAlgorithmNames' + numbers: + type: array + items: + type: integer + exclusiveMinimum: 0 + minItems: 2 + maxItems: 2 + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Algorithm: Pollard's rho + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CommandPollard: + description: |- + Instructions for execution of the Pollard's rho algorithm + type: object + required: + - name + - number + properties: + name: + $ref: '#/components/schemas/EnumAlgorithmNames' + number: + type: integer + exclusiveMinimum: 0 + x-init: + type: integer + default: 2 + minimum: 2 + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Data-type Landscape Geometry, Landscape Values + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + DataTypeLandscapeGeometry: + description: |- + Structure for the geometry of a fitness landscape + type: object + required: + - neighbourhoods + - labels + - values + properties: + neighbourhoods: + $ref: '#/components/schemas/DataTypeLandscapeNeighbourhoods' + labels: + type: array + items: + type: string + minItems: 1 + values: + $ref: '#/components/schemas/DataTypeLandscapeValues' + DataTypeLandscapeNeighbourhoods: + description: |- + Options for the definition of discrete neighbourhoods of a fitness landscape + type: object + required: + - metric + properties: + radius: + type: number + minimum: 1 + default: 1 + metric: + $ref: '#/components/schemas/EnumLandscapeMetric' + DataTypeLandscapeValues: + description: |- + A (potentially multi-dimensional) array of values for the fitness landscape. + oneOf: + - type: array + items: + type: number + - type: array + items: + $ref: '#/components/schemas/DataTypeLandscapeValues' # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Enum Algorithm Names # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -158,6 +302,10 @@ components: - TSP - HIRSCHBERG - RUCKSACK + - RANDOM-WALK + - GENETIC + - EUKLID + - POLLARD-RHO # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Enum Optimise Mode # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -178,3 +326,29 @@ components: enum: - GREEDY - BRANCH-AND-BOUND + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Enum Type of walk mode for fitness walk algorithm + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + EnumWalkMode: + description: |- + Enumeration of walk mode for fitness walk algorithm + - `ADAPTIVE` - points uniformly randomly chosen from nbhd. + - `GRADIENT` - points uniformly randomly chosen amongst points in nbhd with steepest gradient. + - `METROPOLIS` - points uniformly randomly chosen from nbhd. or by entropy. + type: string + enum: + - ADAPTIVE + - GRADIENT + - METROPOLIS + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Enum for metric for neighbourhoods in fitness landscape + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + EnumLandscapeMetric: + description: |- + Enumeration of mode for Rucksack problem + - `MAXIMUM` - `Q` is a neighbour of `P` <==> `max_i d(P_i, Q_i) <= r` + - `MANHATTAN` - `Q` is a neighbour of `P` <==> `sum_i d(P_i, Q_i) <= r` + type: string + enum: + - MAXIMUM + - MANHATTAN diff --git a/code/python/models/config-schema.yaml b/code/python/models/config-schema.yaml index 8829837..5e1d465 100644 --- a/code/python/models/config-schema.yaml +++ b/code/python/models/config-schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 0.2.0 + version: 0.3.0 title: Schemata for config models servers: - url: "." @@ -54,6 +54,10 @@ components: - tarjan - hirschberg - rucksack + - random-walk + - genetic + - euklid + - pollard-rho properties: log-level: $ref: '#/components/schemas/EnumLogLevel' @@ -127,6 +131,38 @@ components: items: $ref: '#/components/schemas/EnumRucksackShow' default: [] + random-walk: + type: object + required: + - verbose + properties: + verbose: + type: boolean + default: false + genetic: + type: object + required: + - verbose + properties: + verbose: + type: boolean + default: false + euklid: + type: object + required: + - verbose + properties: + verbose: + type: boolean + default: false + pollard-rho: + type: object + required: + - verbose + properties: + verbose: + type: boolean + default: false # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Enum LogLevel # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/code/python/pyproject.toml b/code/python/pyproject.toml index ecb93f7..2d34469 100644 --- a/code/python/pyproject.toml +++ b/code/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uni-leipzig-ads-2-2022" -version = "0.2.0" +version = "0.3.0" description = "Zusatzcode, um Algorithmen und Datenstrukturen im Kurs ADS2 zu demonstrieren." authors = [ "Raj Dahya" ] maintainers = [ "raj_mathe" ] diff --git a/code/python/src/algorithms/euklid/__init__.py b/code/python/src/algorithms/euklid/__init__.py new file mode 100644 index 0000000..dca246d --- /dev/null +++ b/code/python/src/algorithms/euklid/__init__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.algorithms.euklid.algorithms import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'euklidean_algorithm', +]; diff --git a/code/python/src/algorithms/euklid/algorithms.py b/code/python/src/algorithms/euklid/algorithms.py new file mode 100644 index 0000000..5befa63 --- /dev/null +++ b/code/python/src/algorithms/euklid/algorithms.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.types import *; +from src.thirdparty.maths import *; + +from models.generated.config import *; +from src.core.utils import *; +from src.models.euklid import *; +from src.algorithms.euklid.display import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'euklidean_algorithm', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD euklidean algorithm +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def euklidean_algorithm( + a: int, + b: int, + verbose: bool = False, +) -> Tuple[int, int, int]: + ''' + Führt den Euklideschen Algorithmus aus, um den größten gemeinsamen Teiler (ggT, en: gcd) + von zwei positiven Zahlen zu berechnen. + ''' + ################ + # NOTE: + # Lemma: gcd(a,b) = gcd(mod(a, b), b) + # Darum immer weiter (a, b) durch (b, gcd(a,b)) ersetzen, bis b == 0. + ################ + steps = []; + d = 0; + while True: + if b == 0: + d = a; + steps.append(Step(a=a, b=b, gcd=d, div=0, rem=a, coeff_a=1, coeff_b=0)); + break; + else: + # Berechne k, r so dass a = k·b + r mit k ≥ 0, 0 ≤ r < b: + r = a % b; + k = math.floor(a / b); + # Speichere Berechnungen: + steps.append(Step(a=a, b=b, gcd=0, div=k, rem=r, coeff_a=0, coeff_b=0)); + # ersetze a, b durch b, r: + a = b; + b = r; + + ################ + # NOTE: + # In jedem step gilt + # a = k·b + r + # und im folgenden gilt: + # d = coeff_a'·a' + coeff_b'·b' + # wobei + # a' = b + # b' = r + # Darum: + # d = coeff_a'·b + coeff_b'·(a - k·b) + # = coeff_b'·a + (coeff_a' - k·coeff_b)·b + # Darum: + # coeff_a = coeff_b' + # coeff_b = coeff_a' - k·coeff_b + ################ + coeff_a = 1; + coeff_b = 0; + for step in steps[::-1][1:]: + (coeff_a, coeff_b) = (coeff_b, coeff_a - step.div * coeff_b); + step.coeff_a = coeff_a; + step.coeff_b = coeff_b; + step.gcd = d; + + if verbose: + step = steps[0]; + repr = display_table(steps=steps, reverse=True); + expr = display_sum(step=step); + print(''); + print('\x1b[1mEuklidescher Algorithmus\x1b[0m'); + print(''); + print(repr); + print(''); + print('\x1b[1mLösung\x1b[0m'); + print(''); + print(f'a=\x1b[1m{step.a}\x1b[0m; b=\x1b[1m{step.b}\x1b[0m; d = \x1b[1m{step.gcd}\x1b[0m = {expr}.'); + print(''); + + return d, coeff_a, coeff_b; diff --git a/code/python/src/algorithms/euklid/display.py b/code/python/src/algorithms/euklid/display.py new file mode 100644 index 0000000..59eab86 --- /dev/null +++ b/code/python/src/algorithms/euklid/display.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; +from src.thirdparty.maths import *; +from src.thirdparty.types import *; + +from src.models.euklid import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'display_table', + 'display_sum', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD display table +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def display_table( + steps: List[Step], + reverse: bool = False, +) -> str: + if reverse: + steps = steps[::-1]; + table = pd.DataFrame({ + 'a': [step.a for step in steps], + 'b': [step.b for step in steps], + 'div': ['-' if step.b == 0 else step.div for step in steps], + 'gcd': [step.gcd for step in steps], + 'expr': [f'= {display_sum(step=step)}' for step in steps], + }) \ + .reset_index(drop=True); + # benutze pandas-Dataframe + tabulate, um schöner darzustellen: + repr = tabulate( + table, + headers=['a', 'b', 'floor(a/b)', 'gcd(a,b)', 'gcd(a,b)=x·a + y·b'], + showindex=False, + colalign=('right', 'right', 'right', 'center', 'left'), + tablefmt='simple' + ); + return repr; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD display table +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def display_sum(step: Step) -> str: + return f'\x1b[1m{step.coeff_a}\x1b[0m·a + \x1b[1m{step.coeff_b}\x1b[0m·b' ; diff --git a/code/python/src/algorithms/genetic/__init__.py b/code/python/src/algorithms/genetic/__init__.py new file mode 100644 index 0000000..b859d69 --- /dev/null +++ b/code/python/src/algorithms/genetic/__init__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.algorithms.genetic.algorithms import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'genetic_algorithm', +]; diff --git a/code/python/src/algorithms/genetic/algorithms.py b/code/python/src/algorithms/genetic/algorithms.py new file mode 100644 index 0000000..5c360b7 --- /dev/null +++ b/code/python/src/algorithms/genetic/algorithms.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.types import *; +from src.thirdparty.maths import *; + +from models.generated.config import *; +from src.core.log import *; +from src.core.utils import *; +from src.models.genetic import *; +from src.algorithms.genetic.display import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'genetic_algorithm', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD genetic algorithm +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def genetic_algorithm( + individual1: List[str], + individual2: List[str], + verbose: bool, +): + ''' + Führt den genetischen Algorithmus auf 2 Individuen aus. + ''' + log_warn('Noch nicht implementiert!'); + return; diff --git a/code/python/src/algorithms/genetic/display.py b/code/python/src/algorithms/genetic/display.py new file mode 100644 index 0000000..1057a07 --- /dev/null +++ b/code/python/src/algorithms/genetic/display.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; +from src.thirdparty.maths import *; +from src.thirdparty.types import *; + +from src.core.log import *; +from src.models.genetic import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'display_table', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD display table +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def display_table( +) -> str: + log_warn('Noch nicht implementiert!'); + return ''; diff --git a/code/python/src/algorithms/hirschberg/__init__.py b/code/python/src/algorithms/hirschberg/__init__.py index 997e89c..0837c21 100644 --- a/code/python/src/algorithms/hirschberg/__init__.py +++ b/code/python/src/algorithms/hirschberg/__init__.py @@ -6,7 +6,6 @@ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ from src.algorithms.hirschberg.algorithms import *; -from src.models.hirschberg.penalties import *; from src.algorithms.hirschberg.display import *; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/code/python/src/algorithms/pollard_rho/__init__.py b/code/python/src/algorithms/pollard_rho/__init__.py new file mode 100644 index 0000000..b072b7e --- /dev/null +++ b/code/python/src/algorithms/pollard_rho/__init__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.algorithms.pollard_rho.algorithms import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'pollard_rho_algorithm', +]; diff --git a/code/python/src/algorithms/pollard_rho/algorithms.py b/code/python/src/algorithms/pollard_rho/algorithms.py new file mode 100644 index 0000000..493d9ca --- /dev/null +++ b/code/python/src/algorithms/pollard_rho/algorithms.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.types import *; +from src.thirdparty.maths import *; + +from models.generated.config import *; +from src.core.utils import *; +from src.models.pollard_rho import *; +from src.algorithms.pollard_rho.display import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'pollard_rho_algorithm', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD pollard's rho algorithm +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def pollard_rho_algorithm( + n: int, + x_init: int = 2, + verbose: bool = False, +): + steps = []; + success = False; + f = lambda _: fct(_, n=n); + + d = 1; + x = y = x_init; + steps.append(Step(x=x)); + k = 0; + k_next = 1; + + while True: + # aktualisiere x: x = f(x_prev): + x = f(x); + # aktualisiere y, wenn k = 2^j: y = x[j] = f(y_prev): + if k == k_next: + k_next = 2*k_next; + y = f(y); + + # ggT berechnen: + d = math.gcd(abs(x-y), n); + steps.append(Step(x=x, y=y, d=d)); + + # Abbruchkriterien prüfen: + if d == 1: # weitermachen, solange d == 1 + k += 1; + continue; + elif d == n: # versagt + success = False; + break; + else: + success = True; + break; + + if verbose: + repr = display_table(steps=steps); + print(''); + print('\x1b[1mEuklidescher Algorithmus\x1b[0m'); + print(''); + print(repr); + print(''); + if success: + print('\x1b[1mBerechneter Faktor:\x1b[0m'); + print(''); + print(f'd = \x1b[1m{d}\x1b[0m.'); + else: + print('\x1b[91mKein (Prim)faktor erkannt!\x1b[0m'); + print(''); + return d; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUXILIARY METHOD function for Pollard's rho +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def fct(x: int, n: int) -> int: + return (x**2 - 1) % n; diff --git a/code/python/src/algorithms/pollard_rho/display.py b/code/python/src/algorithms/pollard_rho/display.py new file mode 100644 index 0000000..6ff792e --- /dev/null +++ b/code/python/src/algorithms/pollard_rho/display.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; +from src.thirdparty.maths import *; +from src.thirdparty.types import *; + +from src.models.pollard_rho import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'display_table', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD display table +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def display_table(steps: List[Step]) -> str: + table = pd.DataFrame({ + 'i': [i for i in range(len(steps))], + 'x': [step.x for step in steps], + 'y': [step.y or '-' for step in steps], + 'd': [step.d or '-' for step in steps], + }) \ + .reset_index(drop=True); + # benutze pandas-Dataframe + tabulate, um schöner darzustellen: + repr = tabulate( + table, + headers=['i', 'x(i)', 'y(i) = x([log₂(i)])', 'gcd(|x - y|,n)'], + showindex=False, + colalign=('right', 'right', 'right', 'center'), + tablefmt='simple', + ); + return repr; diff --git a/code/python/src/algorithms/random_walk/__init__.py b/code/python/src/algorithms/random_walk/__init__.py new file mode 100644 index 0000000..fef358a --- /dev/null +++ b/code/python/src/algorithms/random_walk/__init__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.algorithms.random_walk.algorithms import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'adaptive_walk_algorithm', + 'gradient_walk_algorithm', + 'metropolis_walk_algorithm', +]; diff --git a/code/python/src/algorithms/random_walk/algorithms.py b/code/python/src/algorithms/random_walk/algorithms.py new file mode 100644 index 0000000..744b594 --- /dev/null +++ b/code/python/src/algorithms/random_walk/algorithms.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.maths import *; +from src.thirdparty.plots import *; +from src.thirdparty.types import *; + +from models.generated.config import *; +from models.generated.commands import *; +from src.core.log import *; +from src.core.utils import *; +from src.models.random_walk import *; +from src.algorithms.random_walk.display import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'adaptive_walk_algorithm', + 'gradient_walk_algorithm', + 'metropolis_walk_algorithm', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CONSTANTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MAX_ITERATIONS = 1000; # um endlose Schleifen zu verhindern + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD adaptive walk +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def adaptive_walk_algorithm( + landscape: Landscape, + r: float, + coords_init: tuple, + optimise: EnumOptimiseMode, + verbose: bool, +): + ''' + Führt den Adapative-Walk Algorithmus aus, um ein lokales Minimum zu bestimmen. + ''' + + # lege Fitness- und Umgebungsfunktionen fest: + match optimise: + case EnumOptimiseMode.max: + f = lambda x: -landscape.fitness(*x); + case _: + f = lambda x: landscape.fitness(*x); + nbhd = lambda x: landscape.neighbourhood(*x, r=r, strict=True); + label = lambda x: landscape.label(*x); + + # initialisiere + steps = []; + x = coords_init; + fx = f(x); + fy = fx; + N = nbhd(x); + + # führe walk aus: + k = 0; + while k < MAX_ITERATIONS: + # Wähle zufälligen Punkt und berechne fitness-Wert: + y = uniform_random_choice(N); + fy = f(y); + + # Nur dann aktualisieren, wenn sich f-Wert verbessert: + if fy < fx: + # Punkt + Umgebung + f-Wert aktualisieren + x = y; + fx = fy; + N = nbhd(x); + step = Step(coords=x, label=label(x), improved=True, changed=True); + else: + # Nichts (außer logging) machen! + step = Step(coords=x, label=label(x)); + + # Nur dann (erfolgreich) abbrechen, wenn f-Wert lokal Min: + if fx <= min([f(y) for y in N], default=fx): + step.stopped = True; + steps.append(step); + break; + + steps.append(step); + k += 1; + + return x; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD gradient walk +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def gradient_walk_algorithm( + landscape: Landscape, + r: float, + coords_init: tuple, + optimise: EnumOptimiseMode, + verbose: bool, +): + ''' + Führt den Gradient-Descent (bzw. Ascent) Algorithmus aus, um ein lokales Minimum zu bestimmen. + ''' + + # lege Fitness- und Umgebungsfunktionen fest: + match optimise: + case EnumOptimiseMode.max: + f = lambda x: -landscape.fitness(*x); + case _: + f = lambda x: landscape.fitness(*x); + nbhd = lambda x: landscape.neighbourhood(*x, r=r, strict=True); + label = lambda x: landscape.label(*x); + + # initialisiere + steps = []; + x = coords_init; + fx = landscape.fitness(*x); + fy = fx; + N = nbhd(x); + f_values = [f(y) for y in N]; + fmin = min(f_values); + Z = [y for y, fy in zip(N, f_values) if fy == fmin]; + + # führe walk aus: + k = 0; + while k < MAX_ITERATIONS: + # Wähle zufälligen Punkt mit steilstem Abstieg und berechne fitness-Wert: + y = uniform_random_choice(Z); + fy = fmin; + + # Nur dann aktualisieren, wenn sich f-Wert verbessert: + if fy < fx: + # Punkt + Umgebung + f-Wert aktualisieren + x = y; + fx = fy; + N = nbhd(y); + f_values = [f(y) for y in N]; + fmin = min(f_values); + Z = [y for y, fy in zip(N, f_values) if fy == fmin]; + step = Step(coords=x, label=label(x), improved=True, changed=True); + else: + # Nichts (außer logging) machen! + step = Step(coords=x, label=label(x)); + + # Nur dann (erfolgreich) abbrechen, wenn f-Wert lokal Min: + if fx <= min([f(y) for y in N], default=fx): + step.stopped = True; + steps.append(step); + break; + + steps.append(step); + k += 1; + + return x; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD metropolis walk +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def metropolis_walk_algorithm( + landscape: Landscape, + r: float, + coords_init: tuple, + T: float, + annealing: bool, + optimise: EnumOptimiseMode, + verbose: bool, +): + ''' + Führt den Metropolis-Walk Algorithmus aus, um ein lokales Minimum zu bestimmen. + ''' + + # lege Fitness- und Umgebungsfunktionen fest: + match optimise: + case EnumOptimiseMode.max: + f = lambda x: -landscape.fitness(*x); + case _: + f = lambda x: landscape.fitness(*x); + nbhd = lambda x: landscape.neighbourhood(*x, r=r, strict=True); + label = lambda x: landscape.label(*x); + + # definiere anzahl der hinreichenden Schritt für Stabilität: + n_stable = 2*(3**(landscape.dim) - 1); + + # initialisiere + x = coords_init; + fx = f(x); + fy = fx; + nbhd_x = nbhd(x); + steps = []; + step = Step(coords=x, label=label(x)); + + # führe walk aus: + k = 0; + n_unchanged = 0; + while k < MAX_ITERATIONS: + # Wähle zufälligen Punkt und berechne fitness-Wert: + y = uniform_random_choice(nbhd_x); + fy = f(y); + p = math.exp(-abs(fy-fx)/T); + u = random_binary(p); + + # Aktualisieren, wenn sich f-Wert verbessert + # oder mit einer Wahrscheinlichkeit von p: + if fy < fx or u: + # Punkt + Umgebung + f-Wert aktualisieren + x = y; + fx = fy; + nbhd_x = nbhd(x); + n_unchanged = 0; + step = Step(coords=x, label=label(x), improved=(fy < fx), chance=u, probability=p, changed=True); + else: + # Nichts (außer logging) machen! + n_unchanged += 1; + step = Step(coords=x, label=label(x)); + + # »Temperatur« ggf. abkühlen: + if annealing: + T = cool_temperature(T, k); + + # Nur dann (erfolgreich) abbrechen, wenn f-Wert lokal Min: + if n_unchanged >= n_stable: + step.stopped = True; + steps.append(step); + break; + + steps.append(step); + k += 1; + + if verbose: + for step in steps: + print(step); + + return x; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUXILIARY METHODS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def cool_temperature(T: float, k: int, const: float = 2.) -> float: + harm = const*(k + 1); + return T/(1 + T/harm); diff --git a/code/python/src/algorithms/random_walk/display.py b/code/python/src/algorithms/random_walk/display.py new file mode 100644 index 0000000..a929937 --- /dev/null +++ b/code/python/src/algorithms/random_walk/display.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; +from src.thirdparty.maths import *; +from src.thirdparty.types import *; + +from src.core.log import *; +from src.models.random_walk import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'display_table', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD display table +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def display_table( +) -> str: + log_warn('Noch nicht implementiert!'); + return ''; diff --git a/code/python/src/api.py b/code/python/src/api.py index 28ad4e2..984e489 100644 --- a/code/python/src/api.py +++ b/code/python/src/api.py @@ -40,4 +40,12 @@ def run_command(command: Command) -> Result[CallResult, CallError]: return endpoint_hirschberg(command); elif isinstance(command, CommandRucksack): return endpoint_rucksack(command); + elif isinstance(command, CommandRandomWalk): + return endpoint_random_walk(command); + elif isinstance(command, CommandGenetic): + return endpoint_genetic(command); + elif isinstance(command, CommandEuklid): + return endpoint_euklid(command); + elif isinstance(command, CommandPollard): + return endpoint_pollard_rho(command); raise Exception(f'No endpoint set for `{command.name.value}`-command type.'); diff --git a/code/python/src/endpoints/__init__.py b/code/python/src/endpoints/__init__.py index 4ca27f3..1bb1e79 100644 --- a/code/python/src/endpoints/__init__.py +++ b/code/python/src/endpoints/__init__.py @@ -9,6 +9,10 @@ from src.endpoints.ep_algorithm_hirschberg import *; from src.endpoints.ep_algorithm_tarjan import *; from src.endpoints.ep_algorithm_tsp import *; from src.endpoints.ep_algorithm_rucksack import *; +from src.endpoints.ep_algorithm_genetic import *; +from src.endpoints.ep_algorithm_random_walk import *; +from src.endpoints.ep_algorithm_euklid import *; +from src.endpoints.ep_algorithm_pollard_rho import *; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # EXPORTS @@ -19,4 +23,8 @@ __all__ = [ 'endpoint_tarjan', 'endpoint_tsp', 'endpoint_rucksack', + 'endpoint_random_walk', + 'endpoint_genetic', + 'endpoint_euklid', + 'endpoint_pollard_rho', ]; diff --git a/code/python/src/endpoints/ep_algorithm_euklid.py b/code/python/src/endpoints/ep_algorithm_euklid.py new file mode 100644 index 0000000..2794710 --- /dev/null +++ b/code/python/src/endpoints/ep_algorithm_euklid.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; + +from models.generated.commands import *; +from src.core.calls import *; +from src.setup import config; +from src.algorithms.euklid import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'endpoint_euklid', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ENDPOINT +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@run_safely() +def endpoint_euklid(command: CommandEuklid) -> Result[CallResult, CallError]: + result = euklidean_algorithm( + a = command.numbers[0].__root__, + b = command.numbers[1].__root__, + verbose = config.OPTIONS.euklid.verbose, + ); + return Ok(CallResult(action_taken=True, message=result)); diff --git a/code/python/src/endpoints/ep_algorithm_genetic.py b/code/python/src/endpoints/ep_algorithm_genetic.py new file mode 100644 index 0000000..bac82d1 --- /dev/null +++ b/code/python/src/endpoints/ep_algorithm_genetic.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; + +from models.generated.commands import *; +from src.core.calls import *; +from src.setup import config; +from src.algorithms.genetic import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'endpoint_genetic', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ENDPOINT +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@run_safely() +def endpoint_genetic(command: CommandGenetic) -> Result[CallResult, CallError]: + result = genetic_algorithm( + individual1 = command.population[0], + individual2 = command.population[1], + verbose = config.OPTIONS.genetic.verbose, + ); + return Ok(CallResult(action_taken=True, message=result)); diff --git a/code/python/src/endpoints/ep_algorithm_pollard_rho.py b/code/python/src/endpoints/ep_algorithm_pollard_rho.py new file mode 100644 index 0000000..4674c9c --- /dev/null +++ b/code/python/src/endpoints/ep_algorithm_pollard_rho.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; + +from models.generated.commands import *; +from src.core.calls import *; +from src.setup import config; +from src.algorithms.pollard_rho import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'endpoint_pollard_rho', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ENDPOINT +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@run_safely() +def endpoint_pollard_rho(command: CommandPollard) -> Result[CallResult, CallError]: + result = pollard_rho_algorithm( + n = command.number, + x_init = command.x_init, + verbose = config.OPTIONS.pollard_rho.verbose, + ); + return Ok(CallResult(action_taken=True, message=result)); diff --git a/code/python/src/endpoints/ep_algorithm_random_walk.py b/code/python/src/endpoints/ep_algorithm_random_walk.py new file mode 100644 index 0000000..0324f03 --- /dev/null +++ b/code/python/src/endpoints/ep_algorithm_random_walk.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.code import *; + +from models.generated.commands import *; +from src.core.calls import *; +from src.setup import config; +from src.models.random_walk import *; +from src.algorithms.random_walk import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'endpoint_random_walk', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ENDPOINT +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@run_safely() +def endpoint_random_walk(command: CommandRandomWalk) -> Result[CallResult, CallError]: + # Compute landscape (fitness fct + topology) + initial co-ordinates: + one_based = command.one_based; + landscape = Landscape( + values = command.landscape.values, + labels = command.landscape.labels, + metric = command.landscape.neighbourhoods.metric, + one_based = one_based, + ); + if isinstance(command.coords_init, list): + coords_init = tuple(command.coords_init); + if one_based: + coords_init = tuple(xx - 1 for xx in coords_init); + assert len(coords_init) == landscape.dim, 'Dimension of initial co-ordinations inconsistent with landscape!'; + else: + coords_init = landscape.coords_middle; + + match command.algorithm: + case EnumWalkMode.adaptive: + result = adaptive_walk_algorithm( + landscape = landscape, + r = command.landscape.neighbourhoods.radius, + coords_init = coords_init, + optimise = command.optimise, + verbose = config.OPTIONS.random_walk.verbose + ); + case EnumWalkMode.gradient: + result = gradient_walk_algorithm( + landscape = landscape, + r = command.landscape.neighbourhoods.radius, + coords_init = coords_init, + optimise = command.optimise, + verbose = config.OPTIONS.random_walk.verbose + ); + case EnumWalkMode.metropolis: + result = metropolis_walk_algorithm( + landscape = landscape, + r = command.landscape.neighbourhoods.radius, + coords_init = coords_init, + T = command.temperature_init, + annealing = command.annealing, + optimise = command.optimise, + verbose = config.OPTIONS.random_walk.verbose + ); + case _ as alg: + raise Exception(f'No algorithm implemented for {alg.value}.'); + return Ok(CallResult(action_taken=True, message=result)); diff --git a/code/python/src/endpoints/ep_algorithm_rucksack.py b/code/python/src/endpoints/ep_algorithm_rucksack.py index 0fa4793..9f800c1 100644 --- a/code/python/src/endpoints/ep_algorithm_rucksack.py +++ b/code/python/src/endpoints/ep_algorithm_rucksack.py @@ -50,5 +50,5 @@ def endpoint_rucksack(command: CommandRucksack) -> Result[CallResult, CallError] verbose = config.OPTIONS.rucksack.verbose, ); case _ as alg: - raise Exception(f'No algorithm implemented for {alg.value}.') + raise Exception(f'No algorithm implemented for {alg.value}.'); return Ok(CallResult(action_taken=True, message=result)); diff --git a/code/python/src/models/config/commands.py b/code/python/src/models/config/commands.py index f5d6aef..9d1eb51 100644 --- a/code/python/src/models/config/commands.py +++ b/code/python/src/models/config/commands.py @@ -44,4 +44,12 @@ def interpret_command(command: Command) -> Command: return CommandHirschberg(**command.dict()); case EnumAlgorithmNames.rucksack: return CommandRucksack(**command.dict()); - raise command; + case EnumAlgorithmNames.random_walk: + return CommandRandomWalk(**command.dict()); + case EnumAlgorithmNames.genetic: + return CommandGenetic(**command.dict()); + case EnumAlgorithmNames.euklid: + return CommandEuklid(**command.dict()); + case EnumAlgorithmNames.pollard_rho: + return CommandPollard(**command.dict()); + raise Exception(f'Command type `{command.name.value}` not recognised!'); diff --git a/code/python/src/models/euklid/__init__.py b/code/python/src/models/euklid/__init__.py new file mode 100644 index 0000000..fd2630f --- /dev/null +++ b/code/python/src/models/euklid/__init__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.models.euklid.logging import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'Step', +]; diff --git a/code/python/src/models/euklid/logging.py b/code/python/src/models/euklid/logging.py new file mode 100644 index 0000000..086b26c --- /dev/null +++ b/code/python/src/models/euklid/logging.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.types import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'Step', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CLASS Step +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@dataclass +class Step(): + a: int = field(); + b: int = field(); + gcd: int = field(); + div: int = field(); + rem: int = field(); + coeff_a: int = field(); + coeff_b: int = field(); diff --git a/code/python/src/models/genetic/__init__.py b/code/python/src/models/genetic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/code/python/src/models/pollard_rho/__init__.py b/code/python/src/models/pollard_rho/__init__.py new file mode 100644 index 0000000..c914672 --- /dev/null +++ b/code/python/src/models/pollard_rho/__init__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.models.pollard_rho.logging import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'Step', +]; diff --git a/code/python/src/models/pollard_rho/logging.py b/code/python/src/models/pollard_rho/logging.py new file mode 100644 index 0000000..0bc4463 --- /dev/null +++ b/code/python/src/models/pollard_rho/logging.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.types import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'Step', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CLASS Step +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@dataclass +class Step(): + x: int = field(); + y: Optional[int] = field(default=None); + d: Optional[int] = field(default=None); diff --git a/code/python/src/models/random_walk/__init__.py b/code/python/src/models/random_walk/__init__.py new file mode 100644 index 0000000..250313e --- /dev/null +++ b/code/python/src/models/random_walk/__init__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.models.random_walk.landscape import *; +from src.models.random_walk.logging import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'Landscape', + 'Step', +]; diff --git a/code/python/src/models/random_walk/landscape.py b/code/python/src/models/random_walk/landscape.py new file mode 100644 index 0000000..9332daa --- /dev/null +++ b/code/python/src/models/random_walk/landscape.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from __future__ import annotations; + +from src.thirdparty.maths import *; +from src.thirdparty.misc import *; +from src.thirdparty.types import *; + +from models.generated.commands import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'Landscape', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD fitness function -> Array +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class Landscape(): + _fct: np.ndarray; + _labels: list[str]; + _metric: EnumLandscapeMetric; + _radius: float; + _one_based: bool; + + def __init__( + self, + values: DataTypeLandscapeValues, + labels: List[str], + metric: EnumLandscapeMetric = EnumLandscapeMetric.maximum, + one_based: bool = False, + ): + self._fct = convert_to_nparray(values); + assert len(labels) == self.dim, 'A label is required for each axis/dimension!'; + self._labels = labels; + self._metric = metric; + self._one_based = one_based; + return; + + @property + def shape(self) -> tuple: + return self._fct.shape; + + @property + def dim(self) -> int: + return len(self._fct.shape); + + @property + def coords_middle(self) -> tuple: + return tuple(math.floor(s/2) for s in self.shape); + + @property + def values(self) -> np.ndarray: + return self._fct; + + def fitness(self, *x: int) -> float: + return self._fct[x]; + + def axis_label(self, i: int, x: int) -> str: + if self._one_based: + x = x + 1; + name = self._labels[i]; + return f'{name}{x}'; + + def axis_labels(self, i: int) -> str: + s = self.shape[i]; + return [ self.axis_label(i, x) for x in range(s) ]; + + def label(self, *x: int) -> str: + if self._one_based: + x = tuple(xx + 1 for xx in x); + expr = ','.join([ f'{name}{xx}' for name, xx in zip(self._labels, x)]); + if self.dim > 1: + expr = f'({expr})'; + return expr; + + def neighbourhood(self, *x: int, r: float, strict: bool = False) -> List[tuple]: + r = int(r); + sides = [ + [ xx - j for j in range(1, r+1) if xx - j in range(s) ] + + ([ xx ] if xx in range(s) else []) + + [ xx + j for j in range(1, r+1) if xx + j in range(s) ] + for xx, s in zip(x, self.shape) + ]; + match self._metric: + case EnumLandscapeMetric.maximum: + umg = list(itertools_product(*sides)); + case EnumLandscapeMetric.manhattan: + umg = [ + (*x[:i], xx, *x[(i+1):]) + for i, side in enumerate(sides) + for xx in side + ]; + case _: + umg = [ x ]; + if strict: + umg = [ p for p in umg if p != x ]; + return umg; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUXILIARY METHODS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def convert_to_array(values: DataTypeLandscapeValues) -> list: + return [ + x if isinstance(x, float) else convert_to_array(x) + for x in values.__root__ + ]; + +def convert_to_nparray(values: DataTypeLandscapeValues) -> np.ndarray: + try: + list_of_lists = convert_to_array(values); + return np.asarray(list_of_lists, dtype=float); + except: + raise ValueError('Could not convert to a d-dimensional array! Ensure that the dimensions are consistent.'); diff --git a/code/python/src/models/random_walk/logging.py b/code/python/src/models/random_walk/logging.py new file mode 100644 index 0000000..3bf8953 --- /dev/null +++ b/code/python/src/models/random_walk/logging.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.thirdparty.types import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'Step', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CLASS Step +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@dataclass +class Step(): + coords: tuple = field(); + label: str = field(); + + improved: bool = field(default=False); + chance: bool = field(default=False); + probability: float = field(default=0.); + + changed: bool = field(default=False); + stopped: bool = field(default=False); diff --git a/code/python/src/thirdparty/maths.py b/code/python/src/thirdparty/maths.py index 8bf9eef..21f4cce 100644 --- a/code/python/src/thirdparty/maths.py +++ b/code/python/src/thirdparty/maths.py @@ -8,8 +8,12 @@ from fractions import Fraction; import math; import numpy as np; +from numpy.random import binomial as random_binomial; +random_binary = lambda p: (random_binomial(1, p) == 1); import pandas as pd; import random; +from random import uniform; +from random import choice as uniform_random_choice; from tabulate import tabulate; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -20,7 +24,11 @@ __all__ = [ 'Fraction', 'math', 'np', + 'random_binomial', + 'random_binary', 'pd', 'random', + 'uniform', + 'uniform_random_choice', 'tabulate', ]; diff --git a/code/python/src/thirdparty/misc.py b/code/python/src/thirdparty/misc.py index 8476b32..1aa4dd4 100644 --- a/code/python/src/thirdparty/misc.py +++ b/code/python/src/thirdparty/misc.py @@ -7,6 +7,7 @@ from datetime import datetime; from datetime import timedelta; +from itertools import product as itertools_product; import lorem; import re; from textwrap import dedent; @@ -16,9 +17,10 @@ from textwrap import dedent; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __all__ = [ - 'lorem', 'datetime', 'timedelta', + 'itertools_product', + 'lorem', 're', 'dedent', ]; diff --git a/code/python/src/thirdparty/plots.py b/code/python/src/thirdparty/plots.py new file mode 100644 index 0000000..c05a8fe --- /dev/null +++ b/code/python/src/thirdparty/plots.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from matplotlib import pyplot as mplt; +from matplotlib import animation as mplt_animation; +from matplotlib import colors as mplt_colours; +from matplotlib import patches as mplt_patches; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'mplt', + 'mplt_colours', + 'mplt_patches', + 'mplt_animation', +];