ads2_2022/code/python/src/algorithms/rucksack/display.py

170 lines
6.1 KiB
Python

#!/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.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': order,
'values': values,
'costs': costs,
'margin': [str(Fraction(Fraction(value), Fraction(cost))) 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 = f'{step.bound_subtree:g}';
pad_str = ('' if step.pad == MaskValue.UNSET else step.pad.value);
move_str = ('' if step.move == EnumBranchAndBoundMove.NONE else step.move.value);
if i == index_soln:
move_str = f'{move_str} *';
rows.append({
'bound': f'{step.bound:+g}',
'bound_subtree': expr,
'stack': step.stack_str,
'pad': f'\x1b[2m{pad_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[2mpad?\x1b[0m', '\x1b[2mmove\x1b[0m'],
showindex=False,
colalign=('left', 'left', 'right', 'center', 'left'),
tablefmt='rst'
);
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;
expr = f'\x1b[91m{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[2m{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));
value = sum([ u*x for _, u, x in parts ]);
expr = '\x1b[2m+\x1b[0m'.join(map(render, parts));
if as_maximum:
return f'{value:g} \x1b[2m=\x1b[0m {expr}';
return f'-{value:g} \x1b[2m= -(\x1b[0m{expr}\x1b[2m)\x1b[0m';