From 6f24f4f63539b3d1e411eb475af7a7236c9901c6 Mon Sep 17 00:00:00 2001 From: raj_mathe Date: Fri, 21 Oct 2022 22:08:09 +0200 Subject: [PATCH] master > master: src - widgets --- src/maths/diagrams/sets.py | 7 +- src/maths/sets/random.py | 10 ++ src/thirdparty/render.py | 12 ++- src/widgets/__init__.py | 16 ++++ src/widgets/function_diagrams.py | 154 +++++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 src/widgets/__init__.py create mode 100644 src/widgets/function_diagrams.py diff --git a/src/maths/diagrams/sets.py b/src/maths/diagrams/sets.py index 191e91d..e4e3f9f 100644 --- a/src/maths/diagrams/sets.py +++ b/src/maths/diagrams/sets.py @@ -63,7 +63,7 @@ class Function(Generic[T1,T2]): ]; return getattr(self, '_indexes'); - def draw(self) -> Figure: + def draw(self) -> tuple[Figure, Axes]: return Functions(self).draw(); class Functions: @@ -74,7 +74,7 @@ class Functions: self.fcts = list(f); self.name = r' \circ '.join([ fct.name[0] for fct in self.fcts ][::-1]); - def draw(self, show_labels: bool = True) -> Figure: + def draw(self, show_labels: bool = True) -> tuple[Figure, Axes]: N = len(self.fcts); obj = mplot.subplots(1, 1, constrained_layout=True); fig: Figure = obj[0]; @@ -165,8 +165,7 @@ class Functions: # update range of composition: comp_range = comp_range_next; - - return fig; + return fig, axs; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # AUXILIARY diff --git a/src/maths/sets/random.py b/src/maths/sets/random.py index c3ac611..c1140fe 100644 --- a/src/maths/sets/random.py +++ b/src/maths/sets/random.py @@ -54,6 +54,7 @@ def random_function( Y: list[T2], injective: Optional[bool] = None, surjective: Optional[bool] = None, + force: bool = False, ) -> list[tuple[T1, T2]]: m = len(X); n = len(Y); @@ -61,6 +62,15 @@ def random_function( return []; if n == 0: raise Exception(f'Impossible to create a function with {m} elements in the domain and {n} in the codomain.'); + if not force: + if injective and m > n: + injective = None; + if surjective and m < n: + surjective = None; + if not injective and m == 1: + injective = None; + if not surjective and n == 1: + surjective = None; match (injective, surjective): case (True, _): assert m <= n, f'Impossible to create an injective function with {m} elements in the domain and {n} in the codomain.'; diff --git a/src/thirdparty/render.py b/src/thirdparty/render.py index e41c841..30772c0 100644 --- a/src/thirdparty/render.py +++ b/src/thirdparty/render.py @@ -10,6 +10,7 @@ from IPython.display import display_latex; from IPython.display import display_png; from IPython.display import display_markdown; from IPython.display import display; +import ipywidgets as widgets; # from array_to_latex import to_ltx as array_to_latex; # <- has issues from qiskit.visualization import array_to_latex; @@ -18,10 +19,11 @@ from qiskit.visualization import array_to_latex; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __all__ = [ - 'Latex', - 'display', - 'display_latex', - 'display_png', - 'display_markdown', 'array_to_latex', + 'display_latex', + 'display_markdown', + 'display_png', + 'display', + 'Latex', + 'widgets', ]; diff --git a/src/widgets/__init__.py b/src/widgets/__init__.py new file mode 100644 index 0000000..567f2ec --- /dev/null +++ b/src/widgets/__init__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from src.widgets.function_diagrams import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'FunctionDiagramWidget', +]; diff --git a/src/widgets/function_diagrams.py b/src/widgets/function_diagrams.py new file mode 100644 index 0000000..e370d26 --- /dev/null +++ b/src/widgets/function_diagrams.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# IMPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +from __future__ import annotations; + +from src.thirdparty.types import *; +from src.thirdparty.plots import *; +from src.thirdparty.render import *; + +from src.maths.diagrams import *; +from src.maths.sets import *; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# EXPORTS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__all__ = [ + 'FunctionDiagramWidget', +]; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Class +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class FunctionDiagramWidget(): + state: Optional[widgets.Output]; + N: Optional[int]; + N_max: int; + fnames: list[str]; + setnames: list[str]; + + def __init__( + self, + fnames: list[str], + setnames: list[str], + N: Optional[int] = None, + ): + self.state = None; + self.N = N; + 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; + self.setnames = setnames; + + 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): + cardinalities = [ kwargs[f'card_{k}'] for k in range(N+1)]; + injective = [ kwargs[f'injective_{k}'] for k in range(N)]; + surjective = [ kwargs[f'surjective_{k}'] for k in range(N)]; + X = [ randomset_alphabet(N=card) for card in cardinalities ]; + 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); + # for k in range(N): + # [m, n] = cardinalities[k:][:2]; + # inj = injective[k]; + # surj = surjective[k]; + # if m < n and surj: + # print(f'\x1b[1m{self.fnames[k]}\x1b[0m kann nicht surjektiv sein'); + # if m > n and inj: + # print(f'\x1b[1m{self.fnames[k]}\x1b[0m kann nicht injektiv sein'); + # if n == 1 and not surj: + # print(f'\x1b[1m{self.fnames[k]}\x1b[0m kann nicht nicht-surjektiv sein'); + # if m == 1 and not inj: + # print(f'\x1b[1m{self.fnames[k]}\x1b[0m kann nicht nicht-injektiv sein'); + return; + + def handler_wrapper(self, N: int): + show_labels = widgets.Checkbox(description='Labels anzeigen?', style={'description_width': 'initial'}, visible=True); + control_nr = widgets.IntSlider(value=N); + controls_card = [ + widgets.IntSlider( + value = None, + description = f'|{self.setnames[k]}|', + min = 1, + max = 24, + ) + for k in range(N+1) + ]; + controls_injective = [ + widgets.Checkbox( + value = None, + description = f'{self.fnames[k]} injektiv?', + style = { + 'description_width': 'initial', + }, + visible = True, + ) + for k in range(N) + ]; + controls_surjective = [ + widgets.Checkbox( + value = None, + description = f'{self.fnames[k]} surjectiv?', + style = { + 'description_width': 'initial', + }, + visible = True, + ) + for k in range(N) + ]; + # controls_func = [None for _ in range(2*N)]; + # controls_func[::2] = controls_injective; + # controls_func[1::2] = controls_surjective; + # controls_func = [ + # widgets.RadioButtons( + # options = [ None, 'inj', 'surj', 'bij' ], + # # default: + # value = 'inj', + # layout = { + # 'description_width': 'initial', + # 'width': 'max-content', + # }, + # description = f'{self.fnames[k]}', + # disabled = False, + # ) + # for k in range(N) + # ]; + ui = widgets.VBox( + [show_labels] \ + + controls_card \ + + [ + widgets.HBox([control_injective, control_surjective]) + for control_injective, control_surjective in zip(controls_injective, controls_surjective) + ] + ); + display(ui); + display(widgets.interactive_output( + f = self.handler_plot, + controls = { 'N': control_nr, 'show_labels': show_labels } \ + | { f'card_{k}': controls_card[k] for k in range(N+1) } \ + | { f'injective_{k}': controls_injective[k] for k in range(N) } \ + | { f'surjective_{k}': controls_surjective[k] for k in range(N) } + )); + return;