Compare commits
No commits in common. "ea36c827283e928e616197b7789cb7e6bb06aca2" and "d79b10e1905c8943f61d8a031a65659e5ea59443" have entirely different histories.
ea36c82728
...
d79b10e190
@ -53,7 +53,7 @@
|
|||||||
- name: HIRSCHBERG
|
- name: HIRSCHBERG
|
||||||
word1: 'happily ever after'
|
word1: 'happily ever after'
|
||||||
word2: 'apples'
|
word2: 'apples'
|
||||||
once: false
|
once: true
|
||||||
- name: HIRSCHBERG
|
- name: HIRSCHBERG
|
||||||
word1: 'happily'
|
word1: 'happily'
|
||||||
word2: 'applses'
|
word2: 'applses'
|
||||||
@ -70,34 +70,3 @@
|
|||||||
word1: 'ANSTRENGEN'
|
word1: 'ANSTRENGEN'
|
||||||
word2: 'ANSPANNEN'
|
word2: 'ANSPANNEN'
|
||||||
once: false
|
once: false
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Beispiele für Seminarwoche 11 (Blatt 10)
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- &rucksack_1
|
|
||||||
name: RUCKSACK
|
|
||||||
algorithm: GREEDY
|
|
||||||
allow-fractional: true
|
|
||||||
capacity: 10
|
|
||||||
weights:
|
|
||||||
[3, 4, 5, 2, 1]
|
|
||||||
values:
|
|
||||||
[8, 7, 8, 3, 2]
|
|
||||||
- <<: *rucksack_1
|
|
||||||
algorithm: BRANCH-AND-BOUND
|
|
||||||
- name: RUCKSACK
|
|
||||||
algorithm: BRANCH-AND-BOUND
|
|
||||||
allow-fractional: true
|
|
||||||
capacity: 90
|
|
||||||
items: [
|
|
||||||
'Sonnenblumenkerne',
|
|
||||||
'Buchweizen',
|
|
||||||
'Rote Beete',
|
|
||||||
'Hirse',
|
|
||||||
'Sellerie',
|
|
||||||
]
|
|
||||||
weights:
|
|
||||||
[30, 10, 50, 10, 80]
|
|
||||||
values:
|
|
||||||
[17, 14, 17, 5, 25]
|
|
||||||
|
@ -28,5 +28,3 @@ options:
|
|||||||
show:
|
show:
|
||||||
# - ATOMS
|
# - ATOMS
|
||||||
- TREE
|
- TREE
|
||||||
rucksack:
|
|
||||||
verbose: true
|
|
||||||
|
@ -31,7 +31,7 @@ components:
|
|||||||
$ref: '#/components/schemas/EnumAlgorithmNames'
|
$ref: '#/components/schemas/EnumAlgorithmNames'
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# Algorithm: Tarjan
|
# Command - Algorithm: Tarjan
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
CommandTarjan:
|
CommandTarjan:
|
||||||
description: |-
|
description: |-
|
||||||
@ -63,7 +63,7 @@ components:
|
|||||||
- type: number
|
- type: number
|
||||||
- type: string
|
- type: string
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# Algorithm: TSP
|
# Command - Algorithm: TSP
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
CommandTsp:
|
CommandTsp:
|
||||||
description: |-
|
description: |-
|
||||||
@ -83,9 +83,9 @@ components:
|
|||||||
items:
|
items:
|
||||||
type: number
|
type: number
|
||||||
optimise:
|
optimise:
|
||||||
$ref: '#/components/schemas/EnumOptimiseMode'
|
$ref: '#/components/schemas/EnumTSPOptimise'
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# Algorithm: Hirschberg
|
# Command - Algorithm: Hirschberg
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
CommandHirschberg:
|
CommandHirschberg:
|
||||||
description: |-
|
description: |-
|
||||||
@ -107,46 +107,6 @@ components:
|
|||||||
once:
|
once:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Algorithm: Rucksack Branch & Bound
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
CommandRucksack:
|
|
||||||
description: |-
|
|
||||||
Instructions for execution of Branch & Bound-Algorithm for the Rucksack-Problem
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- algorithm
|
|
||||||
- allow-fractional
|
|
||||||
- capacity
|
|
||||||
- weights
|
|
||||||
- values
|
|
||||||
properties:
|
|
||||||
algorithm:
|
|
||||||
$ref: '#/components/schemas/EnumRucksackAlgorithm'
|
|
||||||
allow-fractional:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
capacity:
|
|
||||||
description: Maximum weight/volumed allowed in rucksack.
|
|
||||||
type: number
|
|
||||||
minimum: 0
|
|
||||||
weights:
|
|
||||||
description: Weights or volumes of each item.
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: number
|
|
||||||
exclusiveMinimum: 0
|
|
||||||
values:
|
|
||||||
description: Value extracted from each item (e.g. monetary).
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: number
|
|
||||||
items:
|
|
||||||
description: Optional names of the items (if empty, defaults to 1-based indexes).
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
default: []
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# Enum Algorithm Names
|
# Enum Algorithm Names
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -158,24 +118,13 @@ components:
|
|||||||
- TARJAN
|
- TARJAN
|
||||||
- TSP
|
- TSP
|
||||||
- HIRSCHBERG
|
- HIRSCHBERG
|
||||||
- RUCKSACK
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# Enum Optimise Mode
|
# Enum TSP - Optimise Mode
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
EnumOptimiseMode:
|
EnumTSPOptimise:
|
||||||
description: |-
|
description: |-
|
||||||
Enumeration of optimisation modi.
|
Enumeration of optimisation options for TSP
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- MIN
|
- MIN
|
||||||
- MAX
|
- MAX
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Enum Rucksack mode for algorithm
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
EnumRucksackAlgorithm:
|
|
||||||
description: |-
|
|
||||||
Enumeration of mode for Rucksack problem
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- GREEDY
|
|
||||||
- BRANCH-AND-BOUND
|
|
||||||
|
@ -53,7 +53,6 @@ components:
|
|||||||
- tsp
|
- tsp
|
||||||
- tarjan
|
- tarjan
|
||||||
- hirschberg
|
- hirschberg
|
||||||
- rucksack-branch-and-bound
|
|
||||||
properties:
|
properties:
|
||||||
log-level:
|
log-level:
|
||||||
$ref: '#/components/schemas/EnumLogLevel'
|
$ref: '#/components/schemas/EnumLogLevel'
|
||||||
@ -115,14 +114,6 @@ components:
|
|||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/EnumHirschbergShow'
|
$ref: '#/components/schemas/EnumHirschbergShow'
|
||||||
default: []
|
default: []
|
||||||
rucksack:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- verbose
|
|
||||||
properties:
|
|
||||||
verbose:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# Enum LogLevel
|
# Enum LogLevel
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from src.algorithms.rucksack.algorithms import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'rucksack_greedy_algorithm',
|
|
||||||
'rucksack_branch_and_bound_algorithm',
|
|
||||||
];
|
|
@ -1,205 +0,0 @@
|
|||||||
#!/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.models.rucksack import *;
|
|
||||||
from src.models.stacks import *;
|
|
||||||
from src.algorithms.rucksack.display import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'rucksack_greedy_algorithm',
|
|
||||||
'rucksack_branch_and_bound_algorithm',
|
|
||||||
];
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# METHOD greedy algorithm
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def rucksack_greedy_algorithm(
|
|
||||||
capacity: float,
|
|
||||||
weights: np.ndarray,
|
|
||||||
values: np.ndarray,
|
|
||||||
items: np.ndarray,
|
|
||||||
fractional: bool,
|
|
||||||
verbose: bool,
|
|
||||||
) -> Solution:
|
|
||||||
'''
|
|
||||||
Durch den Greedy-Algorithm wird der optimale Wert eines Rucksacks
|
|
||||||
unter Rücksicht der Kapizitätsschranke eingeschätzt.
|
|
||||||
|
|
||||||
NOTE: Wenn man `fractional = True` verwendet, liefert der Algorithmus
|
|
||||||
eine obere Schranke des maximalen Wertes beim Originalproblem.
|
|
||||||
'''
|
|
||||||
# sortiere daten:
|
|
||||||
resort_by_value_per_weight(weights=weights, values=values, items=items);
|
|
||||||
|
|
||||||
# führe greedy aus:
|
|
||||||
n = len(weights);
|
|
||||||
weight_total = 0;
|
|
||||||
vector = [ 0 for _ in range(n) ];
|
|
||||||
rucksack = [];
|
|
||||||
for i in range(n):
|
|
||||||
# füge Item i hinzu, solange das Gesamtgewicht noch <= Schranke
|
|
||||||
if weight_total + weights[i] <= capacity:
|
|
||||||
weight_total += weights[i];
|
|
||||||
rucksack.append(i);
|
|
||||||
vector[i] = 1;
|
|
||||||
# sonst abbrechen. Falls Bruchteile erlaubt, füge einen Bruchteil des i. Items hinzu
|
|
||||||
else:
|
|
||||||
if fractional:
|
|
||||||
rucksack.append(i);
|
|
||||||
vector[i] = (capacity - weight_total)/weights[i];
|
|
||||||
break;
|
|
||||||
|
|
||||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
|
||||||
if verbose:
|
|
||||||
repr = display_greedy(vector=vector, values=values);
|
|
||||||
print('');
|
|
||||||
print('\x1b[1mRucksack Problem - Greedy\x1b[0m');
|
|
||||||
print('');
|
|
||||||
print(repr);
|
|
||||||
print('');
|
|
||||||
|
|
||||||
# Lösung ausgeben
|
|
||||||
return Solution(
|
|
||||||
vector = vector,
|
|
||||||
items = items[rucksack].tolist(),
|
|
||||||
weights = weights[rucksack].tolist(),
|
|
||||||
values = values[rucksack].tolist(),
|
|
||||||
);
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# METHOD branch and bound algorithm
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def rucksack_branch_and_bound_algorithm(
|
|
||||||
capacity: float,
|
|
||||||
weights: np.ndarray,
|
|
||||||
values: np.ndarray,
|
|
||||||
items: np.ndarray,
|
|
||||||
verbose: bool,
|
|
||||||
) -> Solution:
|
|
||||||
'''
|
|
||||||
Durch Branch & Bound wird der optimale Wert eines Rucksacks
|
|
||||||
unter Rücksicht der Kapizitätsschranke exakt und effizienter bestimmt.
|
|
||||||
'''
|
|
||||||
|
|
||||||
resort_by_value_per_weight(weights=weights, values=values, items=items);
|
|
||||||
logged_steps = [];
|
|
||||||
|
|
||||||
vector = empty_mask(n=len(weights));
|
|
||||||
lb_estimate = np.inf;
|
|
||||||
S = Stack();
|
|
||||||
S.push(vector);
|
|
||||||
while not S.empty():
|
|
||||||
lb, u = estimate_lower_bound(mask=S.top(), capacity=capacity, weights=weights, values=values, items=items);
|
|
||||||
if verbose:
|
|
||||||
logged_steps.append((lb_estimate, lb, u, str(S)));
|
|
||||||
# Update nur nötig, wenn die (eingeschätzte) untere Schranke von A das bisherige Minimum verbessert:
|
|
||||||
A: Mask = S.pop();
|
|
||||||
if lb < lb_estimate:
|
|
||||||
# Branch:
|
|
||||||
if A.splittable():
|
|
||||||
B, C = A.split();
|
|
||||||
# NOTE: per Wahl erfüllt mind. eine Möglichkeit in B die Kapazitätsschranke.
|
|
||||||
S.push(B);
|
|
||||||
# Nur dann C auf Stack legen, wenn mind. eine Möglichkeit in C die Kapazitätsschranke erfüllt:
|
|
||||||
if sum(weights[C.indexes_one]) <= capacity:
|
|
||||||
S.push(C);
|
|
||||||
# Bound, wenn die Maske den Rucksack komplett bestimmt:
|
|
||||||
else:
|
|
||||||
lb_estimate = lb;
|
|
||||||
vector = A;
|
|
||||||
|
|
||||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
|
||||||
if verbose:
|
|
||||||
repr = display_branch_and_bound(values=values, steps=logged_steps);
|
|
||||||
print('');
|
|
||||||
print('\x1b[1mRucksack Problem - Branch & Bound\x1b[0m');
|
|
||||||
print('');
|
|
||||||
print(repr);
|
|
||||||
print('');
|
|
||||||
|
|
||||||
# Lösung ausgeben
|
|
||||||
rucksack = vector.indexes_one; # Indexes von Items im Rucksack
|
|
||||||
return Solution(
|
|
||||||
vector = vector.decision,
|
|
||||||
items = items[rucksack].tolist(),
|
|
||||||
values = values[rucksack].tolist(),
|
|
||||||
weights = weights[rucksack].tolist(),
|
|
||||||
);
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# AUXILIARY METHOD resort
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def resort_by_value_per_weight(
|
|
||||||
weights: np.ndarray,
|
|
||||||
values: np.ndarray,
|
|
||||||
items: np.ndarray,
|
|
||||||
) -> List[int]:
|
|
||||||
'''
|
|
||||||
Sortiert Daten absteigend nach values/weights.
|
|
||||||
'''
|
|
||||||
n = len(weights);
|
|
||||||
indexes = list(range(n));
|
|
||||||
order = sorted(indexes, key=lambda i: -values[i]/weights[i]);
|
|
||||||
weights[:] = weights[order];
|
|
||||||
values[:] = values[order];
|
|
||||||
items[:] = items[order];
|
|
||||||
return order;
|
|
||||||
|
|
||||||
def estimate_lower_bound(
|
|
||||||
mask: Mask,
|
|
||||||
capacity: float,
|
|
||||||
weights: np.ndarray,
|
|
||||||
values: np.ndarray,
|
|
||||||
items: np.ndarray,
|
|
||||||
) -> Tuple[float, List[float]]:
|
|
||||||
'''
|
|
||||||
Wenn partielle Information über den Rucksack festgelegt ist,
|
|
||||||
kann man bei dem unbekannten Teil das Rucksack-Problem
|
|
||||||
mit Greedy-Algorithmus »lösen«,
|
|
||||||
um schnell eine gute Einschätzung zu bestimmen.
|
|
||||||
|
|
||||||
NOTE: Diese Funktion wird `g(vector)` im Skript bezeichnet.
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Berechnungen bei Items mit bekanntem Status in Rucksack:
|
|
||||||
indexes_one = mask.indexes_one;
|
|
||||||
weight_rucksack = sum(weights[indexes_one]);
|
|
||||||
value_rucksack = sum(values[indexes_one]);
|
|
||||||
|
|
||||||
# Löse mit Greedy-Algorithmus auf Items mit unbekanntem Status.
|
|
||||||
# NOTE: Lösung ist eine Überschätzung des max-Wertes.
|
|
||||||
indexes_unset = mask.indexes_unset;
|
|
||||||
soln_rest = rucksack_greedy_algorithm(
|
|
||||||
capacity = capacity - weight_rucksack, # <- Kapazität = Restgewicht
|
|
||||||
weights = weights[indexes_unset],
|
|
||||||
values = values[indexes_unset],
|
|
||||||
items = items[indexes_unset],
|
|
||||||
fractional = True,
|
|
||||||
verbose = False,
|
|
||||||
);
|
|
||||||
value_rest = soln_rest.total_value;
|
|
||||||
|
|
||||||
# Lösung des [0,1]-Vektors speichern:
|
|
||||||
n = len(mask);
|
|
||||||
vector = np.zeros(shape=(n,), dtype=float);
|
|
||||||
vector[indexes_one] = 1;
|
|
||||||
vector[indexes_unset] = soln_rest.vector;
|
|
||||||
|
|
||||||
# Einschätzung des max-Wertes (Ausgabe mit -1 multiplizieren):
|
|
||||||
value_max_est = value_rucksack + value_rest;
|
|
||||||
return -value_max_est, vector.tolist();
|
|
@ -1,62 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from src.thirdparty.types import *;
|
|
||||||
from src.thirdparty.maths import *;
|
|
||||||
|
|
||||||
from src.models.stacks import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'display_greedy',
|
|
||||||
'display_branch_and_bound',
|
|
||||||
];
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# METHODS display
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def display_greedy(
|
|
||||||
vector: Union[List[int], List[float]],
|
|
||||||
values: np.ndarray,
|
|
||||||
) -> str:
|
|
||||||
value = sum([ u*x for u, x in zip(vector,values)]);
|
|
||||||
expr = '+'.join([
|
|
||||||
f'{x:g}' if u == 1 else f'{Fraction(str(u))}·{x:g}'
|
|
||||||
for u, x in zip(vector,values) if u > 0
|
|
||||||
]);
|
|
||||||
return f'{value:g} (={expr})';
|
|
||||||
|
|
||||||
def display_branch_and_bound(
|
|
||||||
values: np.ndarray,
|
|
||||||
steps: List[Tuple[float, float, Stack]]
|
|
||||||
) -> str:
|
|
||||||
# füge Summen-Ausdrücke für Greedy-Alg hinzu:
|
|
||||||
rows = [];
|
|
||||||
used_vectors = [];
|
|
||||||
for lb_estimate, lb, u, S in steps:
|
|
||||||
if u in used_vectors:
|
|
||||||
rows.append((f'{lb_estimate:g}', f'{lb:g}', S));
|
|
||||||
else:
|
|
||||||
used_vectors.append(u)
|
|
||||||
rows.append((f'{lb_estimate:g}', display_greedy(vector=u, values=values), S));
|
|
||||||
|
|
||||||
table = pd.DataFrame(rows) \
|
|
||||||
.rename(columns={0: 'b', 1: 'g(TOP(S))', 2: 'S'}) \
|
|
||||||
.reset_index(drop=True);
|
|
||||||
# benutze pandas-Dataframe + tabulate, um schöner darzustellen:
|
|
||||||
repr = tabulate(
|
|
||||||
pd.DataFrame(table),
|
|
||||||
headers=['b', 'g(TOP(S))', 'S'],
|
|
||||||
showindex=False,
|
|
||||||
colalign=('left', 'left', 'right'),
|
|
||||||
tablefmt='rst'
|
|
||||||
);
|
|
||||||
return repr;
|
|
@ -38,6 +38,4 @@ def run_command(command: Command) -> Result[CallResult, CallError]:
|
|||||||
return endpoint_tsp(command);
|
return endpoint_tsp(command);
|
||||||
elif isinstance(command, CommandHirschberg):
|
elif isinstance(command, CommandHirschberg):
|
||||||
return endpoint_hirschberg(command);
|
return endpoint_hirschberg(command);
|
||||||
elif isinstance(command, CommandRucksack):
|
|
||||||
return endpoint_rucksack(command);
|
|
||||||
raise Exception(f'No endpoint set for `{command.name.value}`-command type.');
|
raise Exception(f'No endpoint set for `{command.name.value}`-command type.');
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
from src.endpoints.ep_algorithm_hirschberg import *;
|
from src.endpoints.ep_algorithm_hirschberg import *;
|
||||||
from src.endpoints.ep_algorithm_tarjan import *;
|
from src.endpoints.ep_algorithm_tarjan import *;
|
||||||
from src.endpoints.ep_algorithm_tsp import *;
|
from src.endpoints.ep_algorithm_tsp import *;
|
||||||
from src.endpoints.ep_algorithm_rucksack import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# EXPORTS
|
# EXPORTS
|
||||||
@ -18,5 +17,4 @@ __all__ = [
|
|||||||
'endpoint_hirschberg',
|
'endpoint_hirschberg',
|
||||||
'endpoint_tarjan',
|
'endpoint_tarjan',
|
||||||
'endpoint_tsp',
|
'endpoint_tsp',
|
||||||
'endpoint_rucksack',
|
|
||||||
];
|
];
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from src.thirdparty.code import *;
|
|
||||||
from src.thirdparty.maths import *;
|
|
||||||
|
|
||||||
from models.generated.commands import *;
|
|
||||||
from src.core.calls import *;
|
|
||||||
from src.setup import config;
|
|
||||||
from src.algorithms.rucksack import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'endpoint_rucksack',
|
|
||||||
];
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# ENDPOINT
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@run_safely()
|
|
||||||
def endpoint_rucksack(command: CommandRucksack) -> Result[CallResult, CallError]:
|
|
||||||
n = len(command.weights);
|
|
||||||
assert len(command.values) == n, f'Number of weights must be {n}';
|
|
||||||
assert len(command.items) in [0, n], f'Number of items must be 0 or {n}';
|
|
||||||
command.items = command.items or [ str(index + 1) for index in range(n) ];
|
|
||||||
match command.algorithm:
|
|
||||||
case EnumRucksackAlgorithm.greedy:
|
|
||||||
result = rucksack_greedy_algorithm(
|
|
||||||
capacity = command.capacity,
|
|
||||||
weights = np.asarray(command.weights[:]),
|
|
||||||
values = np.asarray(command.values[:]),
|
|
||||||
items = np.asarray(command.items[:]),
|
|
||||||
fractional = command.allow_fractional,
|
|
||||||
verbose = config.OPTIONS.rucksack.verbose,
|
|
||||||
);
|
|
||||||
case EnumRucksackAlgorithm.branch_and_bound:
|
|
||||||
result = rucksack_branch_and_bound_algorithm(
|
|
||||||
capacity = command.capacity,
|
|
||||||
weights = np.asarray(command.weights[:]),
|
|
||||||
values = np.asarray(command.values[:]),
|
|
||||||
items = np.asarray(command.items[:]),
|
|
||||||
verbose = config.OPTIONS.rucksack.verbose,
|
|
||||||
);
|
|
||||||
case _ as alg:
|
|
||||||
raise Exception(f'No algorithm implemented for {alg.value}.')
|
|
||||||
return Ok(CallResult(action_taken=True, message=result));
|
|
@ -29,7 +29,7 @@ __all__ = [
|
|||||||
def endpoint_tsp(command: CommandTsp) -> Result[CallResult, CallError]:
|
def endpoint_tsp(command: CommandTsp) -> Result[CallResult, CallError]:
|
||||||
result = tsp_algorithm(
|
result = tsp_algorithm(
|
||||||
dist = np.asarray(command.dist, dtype=float),
|
dist = np.asarray(command.dist, dtype=float),
|
||||||
optimise = min if command.optimise == EnumOptimiseMode.min else max,
|
optimise = min if command.optimise == EnumTSPOptimise.min else max,
|
||||||
verbose = config.OPTIONS.tsp.verbose,
|
verbose = config.OPTIONS.tsp.verbose,
|
||||||
);
|
);
|
||||||
return Ok(CallResult(action_taken=True, message=result));
|
return Ok(CallResult(action_taken=True, message=result));
|
||||||
|
@ -42,6 +42,4 @@ def interpret_command(command: Command) -> Command:
|
|||||||
return CommandTsp(**command.dict());
|
return CommandTsp(**command.dict());
|
||||||
case EnumAlgorithmNames.hirschberg:
|
case EnumAlgorithmNames.hirschberg:
|
||||||
return CommandHirschberg(**command.dict());
|
return CommandHirschberg(**command.dict());
|
||||||
case EnumAlgorithmNames.rucksack:
|
|
||||||
return CommandRucksack(**command.dict());
|
|
||||||
raise command;
|
raise command;
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from src.models.rucksack.mask import *;
|
|
||||||
from src.models.rucksack.solution import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'empty_mask',
|
|
||||||
'MaskValue',
|
|
||||||
'Mask',
|
|
||||||
'Solution',
|
|
||||||
];
|
|
@ -1,80 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from __future__ import annotations;
|
|
||||||
|
|
||||||
from src.thirdparty.types import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'empty_mask',
|
|
||||||
'MaskValue',
|
|
||||||
'Mask',
|
|
||||||
];
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# ENUMS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
class MaskValue(Enum):
|
|
||||||
ZERO = 0;
|
|
||||||
ONE = 1;
|
|
||||||
UNSET = '*';
|
|
||||||
|
|
||||||
class Mask():
|
|
||||||
index: int;
|
|
||||||
values: List[MaskValue];
|
|
||||||
|
|
||||||
def __init__(self, values: List[MaskValue]):
|
|
||||||
self.values = values;
|
|
||||||
if MaskValue.UNSET in values:
|
|
||||||
self.index = values.index(MaskValue.UNSET);
|
|
||||||
else:
|
|
||||||
self.index = -1;
|
|
||||||
return;
|
|
||||||
|
|
||||||
def __len__(self) -> int:
|
|
||||||
return len(self.values);
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return ''.join([ str(m.value) for m in self.values ]);
|
|
||||||
|
|
||||||
@property
|
|
||||||
def decision(self) -> List[int]:
|
|
||||||
return [ x.value for x in self.values ];
|
|
||||||
|
|
||||||
@property
|
|
||||||
def indexes_set(self) -> List[int]:
|
|
||||||
return [i for i, value in enumerate(self.values) if value != MaskValue.UNSET];
|
|
||||||
|
|
||||||
@property
|
|
||||||
def indexes_one(self) -> List[int]:
|
|
||||||
return [i for i, value in enumerate(self.values) if value == MaskValue.ONE];
|
|
||||||
|
|
||||||
@property
|
|
||||||
def indexes_zero(self) -> List[int]:
|
|
||||||
return [i for i, value in enumerate(self.values) if value == MaskValue.ZERO];
|
|
||||||
|
|
||||||
@property
|
|
||||||
def indexes_unset(self) -> List[int]:
|
|
||||||
return [i for i, value in enumerate(self.values) if value == MaskValue.UNSET];
|
|
||||||
|
|
||||||
def splittable(self) -> bool:
|
|
||||||
return self.index >= 0;
|
|
||||||
|
|
||||||
def split(self) -> Tuple[Mask, Mask]:
|
|
||||||
vector1 = self.values[:];
|
|
||||||
vector1[self.index] = MaskValue.ZERO;
|
|
||||||
vector2 = self.values[:];
|
|
||||||
vector2[self.index] = MaskValue.ONE;
|
|
||||||
return Mask(vector1), Mask(vector2);
|
|
||||||
|
|
||||||
def empty_mask(n: int):
|
|
||||||
return Mask([MaskValue.UNSET for _ in range(n)]);
|
|
@ -1,43 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
from dataclasses import asdict;
|
|
||||||
|
|
||||||
from src.thirdparty.types import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'Solution',
|
|
||||||
];
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# ENUMS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class SolutionRaw():
|
|
||||||
vector: Union[List[int], List[float]] = field();
|
|
||||||
items: List[str] = field();
|
|
||||||
values: List[float] = field(repr=False);
|
|
||||||
weights: List[float] = field(repr=False);
|
|
||||||
|
|
||||||
class Solution(SolutionRaw):
|
|
||||||
@property
|
|
||||||
def support(self) -> List[float]:
|
|
||||||
return [ i for i, v in enumerate(self.vector) if v > 0 ];
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total_weight(self) -> float:
|
|
||||||
return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.weights) ]);
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total_value(self) -> float:
|
|
||||||
return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.values) ]);
|
|
@ -39,13 +39,6 @@ class Stack:
|
|||||||
def __contains__(self, value: Any) -> bool:
|
def __contains__(self, value: Any) -> bool:
|
||||||
return value in self.elements;
|
return value in self.elements;
|
||||||
|
|
||||||
def __iter__(self) -> Generator[Any, None, None]:
|
|
||||||
for value in self.elements:
|
|
||||||
yield value;
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return ', '.join([str(value) for value in self.elements[::-1]]);
|
|
||||||
|
|
||||||
def push(self, value: Any):
|
def push(self, value: Any):
|
||||||
'''
|
'''
|
||||||
add element to stack
|
add element to stack
|
||||||
@ -75,6 +68,3 @@ class Stack:
|
|||||||
checks if element in stack:
|
checks if element in stack:
|
||||||
'''
|
'''
|
||||||
return element in self.elements;
|
return element in self.elements;
|
||||||
|
|
||||||
def empty(self) -> bool:
|
|
||||||
return len(self) == 0;
|
|
||||||
|
2
code/python/src/thirdparty/maths.py
vendored
2
code/python/src/thirdparty/maths.py
vendored
@ -5,7 +5,6 @@
|
|||||||
# IMPORTS
|
# IMPORTS
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
from fractions import Fraction;
|
|
||||||
import math;
|
import math;
|
||||||
import numpy as np;
|
import numpy as np;
|
||||||
import pandas as pd;
|
import pandas as pd;
|
||||||
@ -17,7 +16,6 @@ from tabulate import tabulate;
|
|||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Fraction',
|
|
||||||
'math',
|
'math',
|
||||||
'np',
|
'np',
|
||||||
'pd',
|
'pd',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user