Compare commits

...

3 Commits

Author SHA1 Message Date
RD c2ac8e4c13 master > master: notebook - cleanup 2022-10-23 14:13:30 +02:00
RD dc5918682c master > master: src - cleanup 2022-10-23 14:13:02 +02:00
RD 8d585f96c7 master > master: justfile - logs 2022-10-23 14:12:36 +02:00
4 changed files with 195 additions and 111 deletions

View File

@ -224,6 +224,9 @@ _display-logs:
@- cat logs/debug.log
@echo ""
@echo "----------------"
logs:
@just _create-logs
@tail -f logs/debug.log &
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TARGETS: processes

View File

@ -22,8 +22,9 @@
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"import os;\n",
"import sys;\n",
"import sys; \n",
"\n",
"# NOTE: need this to force jupyter to reload imports:\n",
"for key in list(sys.modules.keys()):\n",
@ -49,11 +50,21 @@
"metadata": {},
"outputs": [],
"source": [
"FunctionDiagramWidget(\n",
" N = 2,\n",
" fnames = ['f', 'g', 'h', 'i', 'j'],\n",
" setnames = ['X', 'Y', 'Z', 'U', 'V', 'W'],\n",
").run();"
"# Userinput - Anzahl der Funktionen\n",
"fnames = ['f', 'g', 'h', 'i', 'j'];\n",
"setnames = ['X', 'Y', 'Z', 'U', 'V', 'W'];\n",
"ctrl = widgets.IntSlider(value=1, description=f'# Funktionen', min=1, max=len(fnames));\n",
"display(ctrl);"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Widget - Mengen und Funktionen\n",
"FunctionDiagramWidget(N=ctrl.value, fnames=fnames, setnames=setnames).run();"
]
},
{

View File

@ -22,9 +22,7 @@ __all__ = [
'GetState',
'CallValue',
'CallError',
'keep_calm_and_carry_on',
'run_safely',
'to_async',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -52,6 +52,12 @@ class FunctionDiagramWidget():
setsfrom: list[list[Any]];
card: list[int];
ctrl_nr: widgets.IntSlider;
btn_refresh: widgets.ToggleButton;
ctrls_card: list[widgets.IntSlider];
ctrls_property: list[widgets.IntSlider];
fcts: Functions;
def __init__(
self,
fnames: list[str],
@ -60,7 +66,7 @@ class FunctionDiagramWidget():
N: Optional[int] = None,
):
self.state = None;
self.N = N;
self.N = N or 1;
self.N_max = len(fnames);
assert len(setnames) == len(fnames) + 1, f'The number of sets must be {self.N_max+1}.';
self.fnames = fnames;
@ -81,35 +87,23 @@ class FunctionDiagramWidget():
self.card = [ min(3, len(XX)) for XX in self.setsfrom ];
def run(self):
if self.N is None:
control_nr = widgets.IntSlider(description=f'# Funktionen', min=1, max=self.N_max);
display(control_nr);
else:
control_nr = widgets.IntSlider(value=self.N);
self.state = widgets.interactive_output(self.handler_wrapper, controls={'N': control_nr});
display(self.state);
def handler_plot(self, N: int, show_labels: bool, **kwargs):
self.card[:(N+1)] = [ kwargs[f'card_{k}'] for k in range(N+1) ];
properties = [ option_to_property(kwargs[f'property_{k}']) for k in range(N)];
injective = list(map(is_injective, properties));
surjective = list(map(is_surjective, properties));
X = [ XX[:card] for XX, card in zip(self.setsfrom[:(N+1)], self.card[:(N+1)]) ];
comp = Functions(*[
Function(
name = (f'{self.fnames[k]}', f'{self.setnames[k]}', f'{self.setnames[k+1]}'),
domain = X[k],
codomain = X[k+1],
fct = random_function(X[k], X[k+1], injective=injective[k], surjective=surjective[k], force=False)
)
for k in range(N)
]);
fig, axs = comp.draw(show_labels=show_labels);
'''
Runs the widget.
'''
self.create_widgets();
display(widgets.interactive_output(
f = self.handler_main,
controls = {'N': self.ctrl_nr},
));
return;
def handler_wrapper(self, N: int):
button_refresh = widgets.ToggleButton(description='Neu laden');
button_show_labels = widgets.Checkbox(
def create_widgets(self):
'''
Initialises the widgets.
'''
N = self.N_max;
self.btn_refresh = widgets.ToggleButton(description='Neu laden');
self.btn_show_labels = widgets.Checkbox(
description = 'Labels anzeigen?',
value = True,
style = {
@ -117,21 +111,31 @@ class FunctionDiagramWidget():
},
visible = True
);
control_nr = widgets.IntSlider(value=N);
controls_card = [
self.ctrl_nr = widgets.IntSlider(value=self.N, description=f'# Funktionen', min=1, max=self.N_max);
cards_max = [ len(XX) for XX in self.setsfrom ];
card_max_max = max(cards_max);
self.ctrls_card = [
widgets.IntSlider(
value = card,
description = f'|{setname}|',
min = 1,
max = len(XX),
max = card_max,
layout = {
'width': 'initial',
# FIXME: scales wrong:
# 'width': f'{100*cards_max/card_max_max}%',
},
)
for setname, XX, card, card_max in zip(
self.setnames[:(N+1)],
self.setsfrom[:(N+1)],
self.card[:(N+1)],
cards_max[:(N+1)],
)
for setname, XX, card in zip(self.setnames[:(N+1)], self.setsfrom[:(N+1)], self.card[:(N+1)])
];
# controls for properties of functions
controls_property = [
self.ctrls_property = [
widgets.RadioButtons(
options = [ e.value for e in possible_properties(all=True) ],
options = [ e.value for e in possible_properties(self.card[k], self.card[k+1]) ],
value = FunctionProperty.NOTHING.value,
layout = {
'description_width': 'initial',
@ -141,59 +145,121 @@ class FunctionDiagramWidget():
)
for k in range(N)
];
# NOTE: dynamically change the possible properties based on the cardinalities.
def update_controls_property(change, k: int):
self.card[k] = change['new'];
if k > 0:
m = self.card[k-1];
n = self.card[k];
controls_property[k-1].options = [ e.value for e in possible_properties(m, n) ];
if k < N:
m = self.card[k];
n = self.card[k+1];
controls_property[k].options = [ e.value for e in possible_properties(m, n) ];
for k, ctrl_card in enumerate(self.ctrls_card):
ctrl_card.observe(partial(self.handler_upd_card, k=k), names='value');
# FIXME: this does not behave correctly:
self.btn_show_labels.observe(partial(self.handler_plot, create=False));
return;
for k, control_card in enumerate(controls_card):
control_card.observe(
partial(update_controls_property, k=k),
names='value',
);
ui = widgets.VBox(
[ button_refresh ] \
+ [ button_show_labels ] \
+ controls_card \
+ [
widgets.HBox(
[
widgets.VBox(
[
widgets.Text(f'Eigenschaften von {fname}'),
control_property
],
layout = {
'description_width': 'initial',
'width': 'initial',
'display': 'flex',
'align_items': 'stretch',
'overflow': 'hidden',
}
)
for fname, control_property in zip(self.fnames[:N], controls_property)
],
)
],
def handler_main(self, *_, **__):
'''
Main handler for updating widgets.
'''
N = self.N = self.ctrl_nr.value;
ui = widgets.VBox([] \
+ [ self.btn_refresh ] \
+ [ self.btn_show_labels ] \
+ [ widgets.Text(f'Größen den Mengen:') ] \
+ self.ctrls_card[:(N+1)] \
+ [widgets.HBox(
[
widgets.VBox(
[
widgets.Text(f'Eigenschaften von {fname}'),
ctrl_property
],
layout = {
'description_width': 'initial',
'width': 'initial',
'display': 'flex',
'align_items': 'stretch',
'overflow': 'hidden',
}
)
for fname, ctrl_property in zip(self.fnames[:N], self.ctrls_property[:N])
],
)],
);
display(ui);
display(widgets.interactive_output(
f = self.handler_plot,
controls = { 'N': control_nr, 'show_labels': button_show_labels } \
| { f'card_{k}': controls_card[k] for k in range(N+1) } \
| { f'property_{k}': controls_property[k] for k in range(N) } \
| { 'refresh': button_refresh }
controls = dict(
N = self.ctrl_nr,
refresh = self.btn_refresh,
# FIXME: shoud not have to rely on this:
show_labels = self.btn_show_labels,
**{
f'card[{k}]': ctrl_card
for k, ctrl_card in enumerate(self.ctrls_card)
},
**{
f'property[{k}]': ctrl_property
for k, ctrl_property in enumerate(self.ctrls_property)
},
),
));
return;
def handler_plot(self, *_, create: bool = True, **__):
'''
Method to create sets, functions, and plot.
'''
self.card = [ ctrl_card.value for ctrl_card in self.ctrls_card ];
if create:
N = self.N;
options = list(map(lambda ctrl: ctrl.value, self.ctrls_property[:N]));
properties = list(map(option_to_property, options));
injective = list(map(is_injective, properties));
surjective = list(map(is_surjective, properties));
sets = [ XX[:card] for XX, card in zip(self.setsfrom[:(N+1)], self.card[:(N+1)]) ];
self.fcts = Functions(*[
Function(
name = (fname, nameX, nameY),
domain = X,
codomain = Y,
fct = random_function(X, Y, injective=inj, surjective=surj, force=False),
)
for fname, nameX, nameY, X, Y, inj, surj in zip(
self.fnames,
self.setnames,
self.setnames[1:],
sets,
sets[1:],
injective,
surjective,
)
]);
# FIXME:
# upon toggling show labels button, this even triggers multiple times,
# and only the old value registers.
show_labels = self.btn_show_labels.value;
fig, axs = self.fcts.draw(show_labels=show_labels);
return;
def handler_upd_card(self, change, k: int):
'''
Handler for altering function property options
upon change of set cardinality.
'''
self.card[k] = change['new'];
def upd(j: int):
m = self.card[j];
n = self.card[j+1];
value = self.ctrls_property[j].value;
options = [ e.value for e in possible_properties(m, n) ];
if value not in options:
value = FunctionProperty.NOTHING.value;
self.ctrls_property[j].options = options;
self.ctrls_property[j].value = value;
return;
if k > 0:
upd(k-1);
if k < self.N:
upd(k);
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUXILIARY METHODS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -204,36 +270,42 @@ def option_to_property(option: str) -> Optional[FunctionProperty]:
except:
return None;
def possible_properties(m: int = 1, n: int = 1, all: bool = False) -> list[FunctionProperty]:
if not all:
if m == 1 and n == 1:
def possible_properties(m: int = 1, n: int = 1) -> list[FunctionProperty]:
if m == n:
if m == 1:
return [
e for e in FunctionProperty
if (is_injective(e) not in [False])
and (is_surjective(e) not in [False])
];
elif m == 1 and n > 1:
else:
return [
e for e in FunctionProperty
if (is_injective(e) not in [False])
and (is_surjective(e) not in [True])
];
elif n == 1 and m > 1:
return [
e for e in FunctionProperty
if (is_surjective(e) not in [False])
and (is_injective(e) not in [True])
];
elif m > n:
return [
e for e in FunctionProperty
if (is_injective(e) not in [True])
];
elif n > m:
return [
e for e in FunctionProperty
if (is_surjective(e) not in [True])
if not (is_surjective(e) in [True] and is_injective(e) in [False])
and not (is_surjective(e) in [False] and is_injective(e) in [True])
];
elif m == 1 and n > 1:
return [
e for e in FunctionProperty
if (is_injective(e) not in [False])
and (is_surjective(e) not in [True])
];
elif n == 1 and m > 1:
return [
e for e in FunctionProperty
if (is_surjective(e) not in [False])
and (is_injective(e) not in [True])
];
elif m > n:
return [
e for e in FunctionProperty
if (is_injective(e) not in [True])
];
elif n > m:
return [
e for e in FunctionProperty
if (is_surjective(e) not in [True])
];
return [ e for e in FunctionProperty ];
def is_injective(property: Optional[FunctionProperty]) -> Optional[bool]: