From 3b8f80cff92a1797ea1f3cd49e3fb285718b1ccb Mon Sep 17 00:00:00 2001 From: raj_mathe Date: Tue, 14 Jun 2022 14:40:02 +0200 Subject: [PATCH] =?UTF-8?q?master=20>=20master:=20code=20py=20-=20verbesse?= =?UTF-8?q?rte=20Darstellung=20+=20=C2=BBkorrekte=C2=AB=20Behandlung=20von?= =?UTF-8?q?=20Reihenfolgen=20-=20im=20Kurs=20wird=20die=20Permutation=20nu?= =?UTF-8?q?r=20f=C3=BCr=20Greedy-Berechnungen=20angewandt=20-=20die=20Reih?= =?UTF-8?q?enfolge=20der=20Items=20in=20der=20Hauptberechnung=20bei=20B&B?= =?UTF-8?q?=20bleibt=20wie=20bei=20Angaben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/python/assets/commands.yaml | 12 +- .../docs/commands/Models/CommandRucksack.md | 14 ++ .../python/docs/commands/Models/CommandTsp.md | 2 +- ...EnumTSPOptimise.md => EnumOptimiseMode.md} | 2 +- .../commands/Models/EnumRucksackAlgorithm.md | 8 ++ code/python/docs/commands/README.md | 4 +- code/python/docs/config/Models/AppOptions.md | 1 + code/python/models/commands-schema.yaml | 14 +- .../src/algorithms/rucksack/algorithms.py | 122 +++++++----------- .../python/src/algorithms/rucksack/display.py | 117 ++++++++++------- .../src/endpoints/ep_algorithm_rucksack.py | 14 +- code/python/src/models/rucksack/solution.py | 6 +- 12 files changed, 169 insertions(+), 147 deletions(-) create mode 100644 code/python/docs/commands/Models/CommandRucksack.md rename code/python/docs/commands/Models/{EnumTSPOptimise.md => EnumOptimiseMode.md} (93%) create mode 100644 code/python/docs/commands/Models/EnumRucksackAlgorithm.md diff --git a/code/python/assets/commands.yaml b/code/python/assets/commands.yaml index e43f1db..a1cbf24 100644 --- a/code/python/assets/commands.yaml +++ b/code/python/assets/commands.yaml @@ -78,18 +78,20 @@ - &rucksack_1 name: RUCKSACK algorithm: GREEDY - allow-fractional: true - capacity: 10 + allow-fractional: false + max-cost: 10 items: [a, b, c, d, e] - weights: + costs: [3, 4, 5, 2, 1] values: [8, 7, 8, 3, 2] +- <<: *rucksack_1 + allow-fractional: true - <<: *rucksack_1 algorithm: BRANCH-AND-BOUND - name: RUCKSACK algorithm: BRANCH-AND-BOUND - capacity: 90 + max-cost: 90 items: [ 'Sonnenblumenkerne', 'Buchweizen', @@ -97,7 +99,7 @@ 'Hirse', 'Sellerie', ] - weights: + costs: [30, 10, 50, 10, 80] values: [17, 14, 17, 5, 25] diff --git a/code/python/docs/commands/Models/CommandRucksack.md b/code/python/docs/commands/Models/CommandRucksack.md new file mode 100644 index 0000000..bae4f88 --- /dev/null +++ b/code/python/docs/commands/Models/CommandRucksack.md @@ -0,0 +1,14 @@ +# CommandRucksack +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**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] +**costs** | [**List**](number.md) | Array of cost for each item (e.g. volume, weight, price, time, etc.). | [default to null] +**values** | [**List**](number.md) | Value extracted from each item (e.g. energy, profit, etc.). | [default to null] +**items** | [**List**](string.md) | Optional names of the items (if empty, defaults to 1-based indexes). | [optional] [default to []] + +[[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/CommandTsp.md b/code/python/docs/commands/Models/CommandTsp.md index ec644a1..f09f65a 100644 --- a/code/python/docs/commands/Models/CommandTsp.md +++ b/code/python/docs/commands/Models/CommandTsp.md @@ -5,7 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **name** | [**EnumAlgorithmNames**](EnumAlgorithmNames.md) | | [default to null] **dist** | [**List**](array.md) | | [default to null] -**optimise** | [**EnumTSPOptimise**](EnumTSPOptimise.md) | | [default to null] +**optimise** | [**EnumOptimiseMode**](EnumOptimiseMode.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/EnumTSPOptimise.md b/code/python/docs/commands/Models/EnumOptimiseMode.md similarity index 93% rename from code/python/docs/commands/Models/EnumTSPOptimise.md rename to code/python/docs/commands/Models/EnumOptimiseMode.md index 7a902a6..ef2487e 100644 --- a/code/python/docs/commands/Models/EnumTSPOptimise.md +++ b/code/python/docs/commands/Models/EnumOptimiseMode.md @@ -1,4 +1,4 @@ -# EnumTSPOptimise +# EnumOptimiseMode ## Properties Name | Type | Description | Notes diff --git a/code/python/docs/commands/Models/EnumRucksackAlgorithm.md b/code/python/docs/commands/Models/EnumRucksackAlgorithm.md new file mode 100644 index 0000000..50b5d77 --- /dev/null +++ b/code/python/docs/commands/Models/EnumRucksackAlgorithm.md @@ -0,0 +1,8 @@ +# EnumRucksackAlgorithm +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[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/README.md b/code/python/docs/commands/README.md index 42fb44b..d9004fd 100644 --- a/code/python/docs/commands/README.md +++ b/code/python/docs/commands/README.md @@ -14,10 +14,12 @@ Class | Method | HTTP request | Description - [Command](.//Models/Command.md) - [CommandHirschberg](.//Models/CommandHirschberg.md) + - [CommandRucksack](.//Models/CommandRucksack.md) - [CommandTarjan](.//Models/CommandTarjan.md) - [CommandTsp](.//Models/CommandTsp.md) - [EnumAlgorithmNames](.//Models/EnumAlgorithmNames.md) - - [EnumTSPOptimise](.//Models/EnumTSPOptimise.md) + - [EnumOptimiseMode](.//Models/EnumOptimiseMode.md) + - [EnumRucksackAlgorithm](.//Models/EnumRucksackAlgorithm.md) diff --git a/code/python/docs/config/Models/AppOptions.md b/code/python/docs/config/Models/AppOptions.md index 8015c77..5f1525a 100644 --- a/code/python/docs/config/Models/AppOptions.md +++ b/code/python/docs/config/Models/AppOptions.md @@ -8,6 +8,7 @@ Name | Type | Description | Notes **tarjan** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null] **tsp** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null] **hirschberg** | [**AppOptions_hirschberg**](AppOptions_hirschberg.md) | | [default to null] +**rucksack** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [optional] [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 a2d7d78..ada6ab3 100644 --- a/code/python/models/commands-schema.yaml +++ b/code/python/models/commands-schema.yaml @@ -116,8 +116,8 @@ components: type: object required: - algorithm - - capacity - - weights + - max-cost + - costs - values properties: algorithm: @@ -125,18 +125,18 @@ components: allow-fractional: type: boolean default: false - capacity: - description: Maximum weight/volumed allowed in rucksack. + max-cost: + description: Upper bound for total cost of rucksack. type: number minimum: 0 - weights: - description: Weights or volumes of each item. + costs: + description: Array of cost for each item (e.g. volume, weight, price, time, etc.). type: array items: type: number exclusiveMinimum: 0 values: - description: Value extracted from each item (e.g. monetary). + description: Value extracted from each item (e.g. energy, profit, etc.). type: array items: type: number diff --git a/code/python/src/algorithms/rucksack/algorithms.py b/code/python/src/algorithms/rucksack/algorithms.py index d774a99..9a58244 100644 --- a/code/python/src/algorithms/rucksack/algorithms.py +++ b/code/python/src/algorithms/rucksack/algorithms.py @@ -28,8 +28,8 @@ __all__ = [ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def rucksack_greedy_algorithm( - capacity: float, - weights: np.ndarray, + max_cost: float, + costs: np.ndarray, values: np.ndarray, items: np.ndarray, fractional: bool, @@ -43,12 +43,11 @@ def rucksack_greedy_algorithm( eine obere Schranke des maximalen Wertes beim Originalproblem. ''' # sortiere daten: - order = resort_by_value_per_weight(weights=weights, values=values, items=items); - uorder = iperm(order); + order = get_sort_order(costs=costs, values=values); # verbose output hier behandeln (irrelevant für Algorithmus): if verbose: - repr = display_order(order=order, weights=weights[uorder], values=values[uorder], items=items[uorder], one_based=True); + repr = display_order(order=order, costs=costs, values=values, items=items, one_based=True); print(''); print('\x1b[1mRucksack Problem - Greedy\x1b[0m'); print(''); @@ -56,46 +55,39 @@ def rucksack_greedy_algorithm( print(''); # führe greedy aus: - n = len(weights); - weight_total = 0; + n = len(costs); + cost_total = 0; vector = [ 0 for _ in range(n) ]; - for i in range(n): + for i in order: # füge Item i hinzu, solange das Gesamtgewicht noch <= Schranke - if weight_total + weights[i] <= capacity: - weight_total += weights[i]; + if cost_total + costs[i] <= max_cost: + cost_total += costs[i]; vector[i] = 1; - # sonst abbrechen. Falls Bruchteile erlaubt, füge einen Bruchteil des i. Items hinzu - else: - if fractional: - vector[i] = (capacity - weight_total)/weights[i]; + # falls Bruchteile erlaubt sind, füge einen Bruchteil des i. Items hinzu und abbrechen + elif fractional: + vector[i] = (max_cost - cost_total)/costs[i]; break; + # ansonsten weiter machen: + else: + continue; # Aspekte der Lösung speichern: rucksack = [i for i, v in enumerate(vector) if v > 0]; # Indexes von Items im Rucksack soln = Solution( vector = vector, items = items[rucksack].tolist(), - weights = weights[rucksack].tolist(), + costs = costs[rucksack].tolist(), values = values[rucksack].tolist(), ); # verbose output hier behandeln (irrelevant für Algorithmus): if verbose: - # Umsortierung rückgängig machen: - vector = [ soln.vector[i] for i in order ]; - rucksack = sorted([ uorder[r] for r in rucksack ]); - permute_data(weights=weights, values=values, items=items, perm=uorder); - # Ausdrücke bestimmen: - expr_value = display_sum(vector=vector, values=values); - expr_weight = display_sum(vector=vector, values=weights); + repr_rucksack = display_rucksack(items=items[rucksack], costs=costs[rucksack], values=values[rucksack]); print('\x1b[1mEingeschätzte Lösung\x1b[0m'); print(''); - if fractional: - print(f'Mask: {soln.vector}'); - print(f' ---> {vector} (unter urspr. Sortierung)'); - print(f'Rucksack: {", ".join(items[rucksack])}.'); - print(f'max. Value ≈ {expr_value}'); - print(f'∑ Weights = {expr_weight}'); + print(f'Mask: {soln.vector}'); + print('Rucksack:') + print(repr_rucksack); print(''); # Lösung ausgeben @@ -106,8 +98,8 @@ def rucksack_greedy_algorithm( # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def rucksack_branch_and_bound_algorithm( - capacity: float, - weights: np.ndarray, + max_cost: float, + costs: np.ndarray, values: np.ndarray, items: np.ndarray, verbose: bool, @@ -117,12 +109,11 @@ def rucksack_branch_and_bound_algorithm( unter Rücksicht der Kapizitätsschranke exakt und effizienter bestimmt. ''' - order = resort_by_value_per_weight(weights=weights, values=values, items=items); - uorder = iperm(order); + order = get_sort_order(costs=costs, values=values); # verbose output hier behandeln (irrelevant für Algorithmus): if verbose: - repr = display_order(order=order, weights=weights[uorder], values=values[uorder], items=items[uorder], one_based=True); + repr = display_order(order=order, costs=costs, values=values, items=items, one_based=True); print(''); print('\x1b[1mRucksack Problem - Branch & Bound\x1b[0m'); print(''); @@ -130,12 +121,12 @@ def rucksack_branch_and_bound_algorithm( print(''); logged_steps = []; - vector = empty_mask(n=len(weights)); + vector = empty_mask(n=len(costs)); lb_estimate = np.inf; S = Stack(); S.push(vector); while not S.empty(): - lb, u, can_add_all, can_add_none = estimate_lower_bound(mask=S.top(), capacity=capacity, weights=weights, values=values, items=items); + lb, u, can_add_all, can_add_none = estimate_lower_bound(mask=S.top(), max_cost=max_cost, costs=costs, values=values, items=items); if verbose: logged_steps.append((lb_estimate, lb, str(S), u, can_add_all, can_add_none)); # Update nur nötig, wenn die (eingeschätzte) untere Schranke von A das bisherige Minimum verbessert: @@ -155,7 +146,7 @@ def rucksack_branch_and_bound_algorithm( B, C = A.split(); 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: + if sum(costs[C.indexes_one]) <= max_cost: S.push(C); # Aspekte der Lösung speichern @@ -164,29 +155,20 @@ def rucksack_branch_and_bound_algorithm( vector = vector.decision, items = items[rucksack].tolist(), values = values[rucksack].tolist(), - weights = weights[rucksack].tolist(), + costs = costs[rucksack].tolist(), ); # verbose output hier behandeln (irrelevant für Algorithmus): if verbose: - # NOTE: Information in Tabelle gemäß permutierten Daten: - repr = display_branch_and_bound(values=values, steps=logged_steps); - # Umsortierung rückgängig machen: - vector = [ soln.vector[i] for i in order ]; - rucksack = sorted([ uorder[r] for r in rucksack ]); - permute_data(weights=weights, values=values, items=items, perm=uorder); - # Ausdrücke bestimmen: - expr_value = display_sum(vector=vector, values=values); - expr_weight = display_sum(vector=vector, values=weights); + repr = display_branch_and_bound(values=values, steps=logged_steps, order=order); + repr_rucksack = display_rucksack(items=items[rucksack], costs=costs[rucksack], values=values[rucksack]); print('\x1b[1mLösung\x1b[0m'); print(''); print(repr); print(''); print(f'Mask: {soln.vector}'); - print(f' ---> {vector} (unter urspr. Sortierung)'); - print(f'Rucksack: {", ".join(items[rucksack])}.'); - print(f'max. Value ≈ {expr_value}'); - print(f'∑ Weights = {expr_weight}'); + print('Rucksack:'); + print(repr_rucksack); print(''); # Lösung ausgeben @@ -196,35 +178,19 @@ def rucksack_branch_and_bound_algorithm( # AUXILIARY METHOD resort # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -def resort_by_value_per_weight( - weights: np.ndarray, - values: np.ndarray, - items: np.ndarray, -) -> List[int]: +def get_sort_order(costs: np.ndarray, values: np.ndarray) -> List[int]: ''' - Sortiert Daten absteigend nach values/weights. + Sortiert Daten absteigend nach values/costs. ''' - n = len(weights); + n = len(costs); indexes = list(range(n)); - order = sorted(indexes, key=lambda i: -values[i]/weights[i]); - permute_data(weights=weights, values=values, items=items, perm=order); + order = sorted(indexes, key=lambda i: -values[i]/costs[i]); return order; -def permute_data( - weights: np.ndarray, - values: np.ndarray, - items: np.ndarray, - perm: List[int], -): - weights[:] = weights[perm]; - values[:] = values[perm]; - items[:] = items[perm]; - return; - def estimate_lower_bound( mask: Mask, - capacity: float, - weights: np.ndarray, + max_cost: float, + costs: np.ndarray, values: np.ndarray, items: np.ndarray, ) -> Tuple[float, List[float], bool]: @@ -243,19 +209,19 @@ def estimate_lower_bound( # Berechnungen bei Items mit bekanntem Status in Rucksack: value_rucksack = sum(values[indexes_one]); - weight_rucksack = sum(weights[indexes_one]); + cost_rucksack = sum(costs[indexes_one]); vector[indexes_one] = 1; # Für Rest des Rucksacks (Items mit unbekanntem Status): - weight_rest = capacity - weight_rucksack; + cost_rest = max_cost - cost_rucksack; can_add_all = False; can_add_none = False; # Prüfe, ob man als Lösung alles/nichts hinzufügen kann: - if len(indexes_unset) > 0 and sum(weights[indexes_unset]) <= weight_rest: + if len(indexes_unset) > 0 and sum(costs[indexes_unset]) <= cost_rest: can_add_all = True; vector[indexes_unset] = 1; value_rest = sum(values[indexes_unset]); - elif len(indexes_unset) > 0 and min(weights[indexes_unset]) > weight_rest: + elif len(indexes_unset) > 0 and min(costs[indexes_unset]) > cost_rest: can_add_none = True; vector[indexes_unset] = 0; value_rest = 0; @@ -263,8 +229,8 @@ def estimate_lower_bound( # NOTE: Lösung ist eine Überschätzung des max-Wertes. else: soln_rest = rucksack_greedy_algorithm( - capacity = weight_rest, # <- Kapazität = Restgewicht - weights = weights[indexes_unset], + max_cost = cost_rest, # <- Kapazität = Restgewicht + costs = costs[indexes_unset], values = values[indexes_unset], items = items[indexes_unset], fractional = True, diff --git a/code/python/src/algorithms/rucksack/display.py b/code/python/src/algorithms/rucksack/display.py index 649eb43..d433e55 100644 --- a/code/python/src/algorithms/rucksack/display.py +++ b/code/python/src/algorithms/rucksack/display.py @@ -17,8 +17,9 @@ from src.models.stacks import *; __all__ = [ 'display_order', - 'display_sum', + 'display_rucksack', 'display_branch_and_bound', + 'display_sum', ]; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -27,31 +28,88 @@ __all__ = [ def display_order( order: List[int], - weights: np.ndarray, + costs: np.ndarray, values: np.ndarray, items: np.ndarray, one_based: bool = False, ) -> str: table = pd.DataFrame({ - 'items': items, - 'order': order, - 'values': values, - 'weights': weights, - 'u': (values/weights), + 'items': items, + 'order': order, + 'values': values, + 'costs': costs, + 'u': (values/costs), }) \ .reset_index(drop=True); if one_based: table['order'] += 1; # benutze pandas-Dataframe + tabulate, um schöner darzustellen: repr = tabulate( - pd.DataFrame(table), - headers=['item', 'greedy order', 'value', 'weight', 'value/weight'], + table, + headers=['item', 'greedy order', 'value', 'cost', 'value/cost'], showindex=False, colalign=('left', 'center', 'center', 'center', 'right'), tablefmt='rst' ); return repr; +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD display rucksack +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def display_rucksack( + items: np.ndarray, + costs: np.ndarray, + values: np.ndarray, +) -> str: + table = pd.DataFrame({ + 'items': items.tolist() + ['----', '∑'], + 'costs': costs.tolist() + ['', f'\x1b[92;1m{sum(costs)}\x1b[0m'], + 'values': values.tolist() + ['', f'\x1b[92;1m{sum(values)}\x1b[0m'], + }); + repr = tabulate( + table, + headers=['item', 'cost', 'value'], + showindex=False, + colalign=('left', 'center', 'center'), + tablefmt='rst' + ); + return repr; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHOD display result of branch and bound +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def display_branch_and_bound( + values: np.ndarray, + steps: List[Tuple[float, float, Stack, List[float], bool, bool]], + order: Optional[List[int]] = None, +) -> str: + # füge Summen-Ausdrücke für Greedy-Alg hinzu: + rows = []; + used_vectors = []; + for lb_estimate, lb, S, u, can_add_all, can_add_none in steps: + pad = '1' if can_add_all else ('0' if can_add_none else ''); + if u in used_vectors: + expr = f'{lb:g}'; + else: + used_vectors.append(u) + expr = display_sum(vector=u, values=values, as_maximum=False, order=order); + rows.append((f'{lb_estimate:g}', expr, pad, S)); + + table = pd.DataFrame(rows) \ + .rename(columns={0: 'b', 1: 'g(TOP(S))', 2: 'pad?', 3: 'S'}) \ + .reset_index(drop=True); + # benutze pandas-Dataframe + tabulate, um schöner darzustellen: + repr = tabulate( + table, + headers=['b', 'g(TOP(S))', 'pad?', 'S'], + showindex=False, + colalign=('left', 'left', 'center', 'right'), + tablefmt='rst' + ); + return repr; + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # METHOD display sum # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -59,47 +117,18 @@ def display_order( def display_sum( vector: List[float], values: np.ndarray, + order: Optional[List[int]] = None, as_maximum: bool = True, ) -> str: - value = sum([ u*x for u, x in zip(vector,values)]); + parts = [ (u, x) for u, x in zip(vector, values)]; + if not (order is None): + parts = [ parts[j] for j in order ]; + value = sum([ u*x for u, x in parts]); 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 + for u, x in parts if u > 0 ]); if as_maximum: return f'{value:g} = {expr}'; else: return f'-{value:g} = -({expr})'; - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# METHOD display result of branch and bound -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -def display_branch_and_bound( - values: np.ndarray, - steps: List[Tuple[float, float, Stack, List[float], bool, bool]] -) -> str: - # füge Summen-Ausdrücke für Greedy-Alg hinzu: - rows = []; - used_vectors = []; - for lb_estimate, lb, S, u, can_add_all, can_add_none in steps: - pad = '1' if can_add_all else ('0' if can_add_none else ''); - if u in used_vectors: - expr = f'{lb:g}'; - else: - used_vectors.append(u) - expr = display_sum(vector=u, values=values, as_maximum=False); - rows.append((f'{lb_estimate:g}', expr, pad, S)); - - table = pd.DataFrame(rows) \ - .rename(columns={0: 'b', 1: 'g(TOP(S))', 2: 'pad?', 3: 'S'}) \ - .reset_index(drop=True); - # benutze pandas-Dataframe + tabulate, um schöner darzustellen: - repr = tabulate( - pd.DataFrame(table), - headers=['b', 'g(TOP(S))', 'pad?', 'S'], - showindex=False, - colalign=('left', 'left', 'center', 'right'), - tablefmt='rst' - ); - return repr; diff --git a/code/python/src/endpoints/ep_algorithm_rucksack.py b/code/python/src/endpoints/ep_algorithm_rucksack.py index 35e4702..0fa4793 100644 --- a/code/python/src/endpoints/ep_algorithm_rucksack.py +++ b/code/python/src/endpoints/ep_algorithm_rucksack.py @@ -27,15 +27,15 @@ __all__ = [ @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}'; + n = len(command.costs); + assert len(command.values) == n, 'Number of values and costs must coincide!'; + 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[:]), + max_cost = command.max_cost, + costs = np.asarray(command.costs[:]), values = np.asarray(command.values[:]), items = np.asarray(command.items[:]), fractional = command.allow_fractional, @@ -43,8 +43,8 @@ def endpoint_rucksack(command: CommandRucksack) -> Result[CallResult, CallError] ); case EnumRucksackAlgorithm.branch_and_bound: result = rucksack_branch_and_bound_algorithm( - capacity = command.capacity, - weights = np.asarray(command.weights[:]), + max_cost = command.max_cost, + costs = np.asarray(command.costs[:]), values = np.asarray(command.values[:]), items = np.asarray(command.items[:]), verbose = config.OPTIONS.rucksack.verbose, diff --git a/code/python/src/models/rucksack/solution.py b/code/python/src/models/rucksack/solution.py index 269f5e3..3d39462 100644 --- a/code/python/src/models/rucksack/solution.py +++ b/code/python/src/models/rucksack/solution.py @@ -24,10 +24,10 @@ __all__ = [ @dataclass class SolutionRaw(): - vector: Union[List[int], List[float]] = field(); + vector: List[float] = field(); items: List[str] = field(); values: List[float] = field(repr=False); - weights: List[float] = field(repr=False); + costs: List[float] = field(repr=False); class Solution(SolutionRaw): @property @@ -40,7 +40,7 @@ class Solution(SolutionRaw): @property def total_weight(self) -> float: - return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.weights) ]); + return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.costs) ]); @property def total_value(self) -> float: