#!/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.utils import *; from src.setup import config; from models.generated.config import *; from src.models.stacks import *; from src.models.rucksack import *; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # EXPORTS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __all__ = [ 'display_order', 'display_rucksack', 'display_branch_and_bound', 'display_sum', ]; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # METHOD display order # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def display_order( order: List[int], costs: np.ndarray, values: np.ndarray, items: np.ndarray, one_based: bool = False, ) -> str: table = pd.DataFrame({ 'items': items, 'order': iperm(order), 'values': values, 'costs': costs, 'margin': [f'{value/cost:.6f}' for cost, value in zip(costs, values)], }) \ .reset_index(drop=True); if one_based: table['order'] += 1; # benutze pandas-Dataframe + tabulate, um schöner darzustellen: repr = tabulate( 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, choice: List[Fraction], ) -> str: show_options = config.OPTIONS.rucksack.show; render = lambda r: f'{r:g}'; choice = np.asarray(choice); rucksack = np.where(choice > 0); if not(EnumRucksackShow.all_weights in show_options): items = items[rucksack]; costs = costs[rucksack]; values = values[rucksack]; choice = choice[rucksack]; table = pd.DataFrame({ 'items': items.tolist() + ['----', '∑'], 'nr': list(map(str, choice)) + ['----', f'\x1b[92;1m{float(sum(choice)):g}\x1b[0m'], 'costs': list(map(render, costs)) + ['----', f'\x1b[92;1m{sum(choice*costs):g}\x1b[0m'], 'values': list(map(render, values)) + ['----', f'\x1b[92;1m{sum(choice*values):g}\x1b[0m'], }); repr = tabulate( table, headers=['item', 'nr', 'cost', 'value'], showindex=False, colalign=('left', 'center', 'center', 'center'), tablefmt='rst' ); return repr; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # METHOD display result of branch and bound # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def display_branch_and_bound(values: np.ndarray, steps: List[Step]) -> str: show_options = config.OPTIONS.rucksack.show; show_all_sums = (EnumRucksackShow.all_sums in show_options); rows = []; used_choices = []; index_soln = max([-1] + [ i for i, step in enumerate(steps) if step.move == EnumBranchAndBoundMove.BOUND ]); for i, step in enumerate(steps): if show_all_sums or step.choice not in used_choices: # Füge Summen-Ausdrücke für Greedy-Alg hinzu: used_choices.append(step.choice); expr = display_sum(choice=step.choice, values=values, as_maximum=False, order=step.order, indexes=step.indexes); else: expr = ''; bound_str = f'{step.bound:+g}'; solution_str = f'{step.solution or ""}'; move_str = ('' if step.move == EnumBranchAndBoundMove.NONE else step.move.value); if i == index_soln: bound_str = f'* \x1b[92;1m{bound_str}\x1b[0m'; rows.append({ 'bound': f'{bound_str}', 'bound_subtree': f'{step.bound_subtree:g}', 'bound_subtree_sum': expr, 'stack': step.stack_str, 'solution': f'\x1b[2m{solution_str}\x1b[0m', 'move': f'\x1b[2m{move_str}\x1b[0m', }); table = pd.DataFrame(rows).reset_index(drop=True); # benutze pandas-Dataframe + tabulate, um schöner darzustellen: repr = tabulate( table, headers=['bound', 'g(TOP(S))', '', 'S — stack', '\x1b[2msoln\x1b[0m', '\x1b[2mmove\x1b[0m'], showindex=False, colalign=('right', 'right', 'left', 'right', 'center', 'left'), tablefmt='simple' ); return repr; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # METHOD display sum # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def display_sum( choice: List[Fraction], values: np.ndarray, order: Optional[List[int]] = None, indexes: List[int] = [], as_maximum: bool = True, ) -> str: show_options = config.OPTIONS.rucksack.show; show_all_weights = (EnumRucksackShow.all_weights in show_options); def render(x: Tuple[bool, Fraction, float]): b, u, value = x; if u == 0: expr = f'\x1b[94;2m{value:g}\x1b[0m' if b else f'\x1b[2m{value:g}\x1b[0m'; else: expr = f'\x1b[94m{value:g}\x1b[0m' if b else f'\x1b[0m{value:g}\x1b[0m'; if not show_all_weights and u == 1: return expr; return f'\x1b[2;4m{u}\x1b[0m\x1b[2m·\x1b[0m{expr}'; parts = [ (i in indexes, u, x) for i, (u, x) in enumerate(zip(choice, values)) ]; if not (order is None): parts = [ parts[j] for j in order ]; if not show_all_weights: parts = list(filter(lambda x: x[1] > 0, parts)); expr = '\x1b[2m + \x1b[0m'.join(map(render, parts)); if as_maximum: return f'\x1b[2m=\x1b[0m {expr}'; return f'\x1b[2m= -(\x1b[0m{expr}\x1b[2m)\x1b[0m';