Compare commits

...

4 Commits

Author SHA1 Message Date
RD 7b237246da master > master: notebook woche 2 2022-10-20 16:22:22 +02:00
RD bad02def40 master > master: justfile - gen models param 2022-10-20 16:22:10 +02:00
RD e6f2d8afea master > master: requirements 2022-10-20 16:21:57 +02:00
RD 5e307eb355 master > master: src 2022-10-20 16:21:50 +02:00
11 changed files with 483 additions and 69 deletions

View File

@ -68,6 +68,7 @@ _generate-models path name:
--encoding "UTF-8" \
--disable-timestamp \
--use-schema-description \
--set-default-enum-member \
--allow-population-by-field-name \
--snake-case-field \
--strict-nullable \

View File

@ -1,67 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Woche 0 - 10.-16. Oktober 2022 #\n",
"\n",
"Herzlich willkommen zur 0. Woche!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os;\n",
"import sys;\n",
"\n",
"# NOTE: need this to force jupyter to reload imports:\n",
"for key in list(sys.modules.keys()):\n",
" if key.startswith('src.'):\n",
" del sys.modules[key];\n",
"\n",
"os.chdir(os.path.dirname(_dh[0]));\n",
"sys.path.insert(0, os.getcwd());\n",
"\n",
"from src.thirdparty.maths import *;\n",
"from src.thirdparty.render import *;\n",
"\n",
"np.random.seed(8007253); # zur Wiederholbarkeit"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"A = np.random.randint(-100, 100, size=(4, 7));\n",
"display(array_to_latex(A, precision=3));"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.10.6 64-bit",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

100
notebooks/week-2.ipynb Normal file
View File

@ -0,0 +1,100 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Woche 2 - 17.-23. Oktober 2022 #\n",
"\n",
"Herzlich willkommen zur 2. Woche!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os;\n",
"import sys;\n",
"\n",
"# NOTE: need this to force jupyter to reload imports:\n",
"for key in list(sys.modules.keys()):\n",
" if key.startswith('src.'):\n",
" del sys.modules[key];\n",
"\n",
"os.chdir(os.path.dirname(_dh[0]));\n",
"sys.path.insert(0, os.getcwd());\n",
"\n",
"from src.thirdparty.maths import *;\n",
"from src.thirdparty.render import *;\n",
"\n",
"from src.maths.diagrams import *;\n",
"from src.maths.sets import *;\n",
"\n",
"np.random.seed(8007253); # zur Wiederholbarkeit"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"A = np.random.randint(-100, 100, size=(4, 7));\n",
"\n",
"X = ['a', 'b', 'c', 'd', 'e'];\n",
"Y = [2, 3, 5, 7, 11, 13, 17];\n",
"Z = ['α', 'β', 'γ', 'δ'];\n",
"f = [('a', 5), ('b', 5), ('c', 17), ('d', 2)]\n",
"g = [(2, 'γ'), (3, 'α'), (5, 'β'), (7, 'δ'), (11, 'β'), (13, 'γ'), (17, 'β')];\n",
"comp = Functions(\n",
" Function(name=('$f$', 'X', 'Y'), domain=X, codomain=Y, fct=f),\n",
" Function(name=('$g$', 'Y', 'Z'), domain=Y, codomain=Z, fct=g),\n",
");\n",
"comp.draw(show_labels=True);"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"A = np.random.randint(-100, 100, size=(4, 7));\n",
"\n",
"X = randomset_alphabet(low=2, high=5);\n",
"Y = randomset_integers(low=2, high=5);\n",
"Z = randomset_greek(low=2, high=5);\n",
"f = random_function(X, Y);\n",
"g = random_function(Y, Z);\n",
"comp = Functions(\n",
" Function(name=('$f$', 'X', 'Y'), domain=X, codomain=Y, fct=f),\n",
" Function(name=('$g$', 'Y', 'Z'), domain=Y, codomain=Z, fct=g),\n",
");\n",
"comp.draw(show_labels=False);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.10.8 64-bit",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.8"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -40,7 +40,7 @@ datamodel-code-generator>=0.12.0
# maths
fraction>=2.2.0
numpy>=1.22.4
matplotlib>=3.5.1
matplotlib>=3.6.1
# tables, data frames
pandas>=1.4.2

0
src/maths/__init__.py Normal file
View File

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.maths.diagrams.sets import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'Function',
'Functions',
];

205
src/maths/diagrams/sets.py Normal file
View File

@ -0,0 +1,205 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations;
from src.thirdparty.code import *;
from src.thirdparty.types import *;
from src.thirdparty.maths import *;
from src.thirdparty.plots import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'Function',
'Functions',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CONSTANTS / VARIABLES
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
T1 = TypeVar('T1');
T2 = TypeVar('T2');
SCALE = (1., 4.);
OFFSET = (3., 0.);
MARGIN = 0.1;
N_RESOLUTION = 100;
ANNOTATE_OFFSET = (0, 10);
FONTSIZE_PTS = 10;
FONTSIZE_FCT = 14;
FONTSIZE_SETS = 14;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Classes
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataclass
class Function(Generic[T1,T2]):
name: tuple[str, str, str] = field();
domain: list[T1] = field();
codomain: list[T2] = field();
fct: list[tuple[T1,T2]] = field();
@property
def range(self) -> list[T2]:
return [y for x, y in self.fct];
@property
def indexes(self) -> list[tuple[int, int]]:
# prevent repeated computation:
if not hasattr(self, '_indexes'):
self._indexes = [
(self.domain.index(x), self.codomain.index(y))
for x, y in self.fct
];
return getattr(self, '_indexes');
def draw(self) -> Figure:
return Functions(self).draw();
class Functions:
fcts: list[Function];
def __init__(self, *f: Function):
self.fcts = list(f);
def draw(self, show_labels: bool = True) -> Figure:
N = len(self.fcts);
obj = mplot.subplots(1, 1, constrained_layout=True);
fig: Figure = obj[0];
axs: Axes = obj[1];
axs.tick_params(axis='both', which='both', left=False, right=False, top=False, bottom=False, labelbottom=False, labelleft=False);
mplot.title('');
mplot.xlabel('');
mplot.ylabel('');
mplot.margins(x=MARGIN, y=MARGIN);
origin = np.asarray((0., 0.));
offset = np.asarray(OFFSET);
p_set = oval(nr_points=N_RESOLUTION, scale=SCALE, centre=origin);
for k in range(N+1):
axs.plot(p_set[:, 0] + k*offset[0], p_set[:, 1] + k*offset[1], label='', color='blue');
p_domain = [];
p_codomain = [];
comp_range = [];
anchors = [
[
# function name
origin + (k + 0.5)*offset + (0, -1.1*SCALE[1]),
# sets
origin + k * offset + (0, 1.1 * SCALE[1]),
origin + (k + 1) * offset + (0, 1.1 * SCALE[1]),
# arrow start -> end
origin + (k + 0.05) * offset + (0, -1.1 * SCALE[1]),
origin + (k + 1 - 0.05) * offset + (0, -1.1 * SCALE[1]),
]
for k in range(N)
];
for k, f in enumerate(self.fcts):
if k == 0:
comp_range = f.domain;
p_domain = random_points(nr_points=len(f.domain), scale=SCALE, centre=origin + k*offset);
else:
p_domain = p_codomain;
p_codomain = random_points(nr_points=len(f.codomain), scale=SCALE, centre=origin + (k+1)*offset);
# range of composition so far:
comp_range_next = [y for x, y in f.fct if x in comp_range];
if k == 0:
axs.scatter(p_domain[:, 0], p_domain[:, 1], label='', color='black', marker='o');
if show_labels:
for i, p in enumerate(p_domain):
x_name = f.domain[i];
axs.annotate(text=f'{x_name}', xy = p, textcoords='offset points', xytext=ANNOTATE_OFFSET, ha='center', size=FONTSIZE_PTS);
for j, p in enumerate(p_codomain):
y = f.codomain[j];
marker = 'o' if (y in comp_range_next) else 'x';
axs.scatter([p[0]], [p[1]], label='', color='black', marker=marker);
y_name = f.codomain[j];
if show_labels:
axs.annotate(text=f'{y_name}', xy=p, textcoords='offset points', xytext=ANNOTATE_OFFSET, ha='center', size=FONTSIZE_PTS);
for i, j in f.indexes:
p = p_domain[i];
q = p_codomain[j];
x = f.domain[i];
if k == 0 or (x in comp_range):
axs.plot([p[0], q[0]], [p[1], q[1]], label='', color='g', linewidth=2);
else:
axs.plot([p[0], q[0]], [p[1], q[1]], label='', color='g', linestyle='--', linewidth=1);
anchor = anchors[k];
fct_name, X_name, Y_name = f.name;
axs.annotate(text=f'{fct_name}', xy=anchor[0], ha='center', size=FONTSIZE_FCT);
if k == 0:
axs.annotate(text=f'{X_name}', xy=anchor[1], ha='center', size=FONTSIZE_FCT);
axs.annotate(text=f'{Y_name}', xy=anchor[2], ha='center', size=FONTSIZE_FCT);
axs.add_patch(FancyArrowPatch(
anchor[3], anchor[4],
connectionstyle = 'arc3,rad=0.5',
arrowstyle = 'Simple, tail_width=0.5, head_width=4, head_length=8',
color = 'black',
));
# update range of composition:
comp_range = comp_range_next;
return fig;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUXILIARY
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def oval(
nr_points: int,
scale: tuple[float, float] = (1., 1.),
centre: tuple[float, float] = (0., 0.),
) -> NDArray[Shape['*, 2'], Float]:
theta = np.linspace(start=0, stop=2*np.pi, num=nr_points, endpoint=True);
P = np.zeros(shape=(nr_points, 2), dtype=float);
P[:, 0] = centre[0] + scale[0] * np.cos(theta);
P[:, 1] = centre[1] + scale[1] * np.sin(theta);
P[-1, :] = P[0, :];
return P;
def random_points(
nr_points: int,
scale: tuple[float, float] = (1., 1.),
centre: tuple[float, float] = (0., 0.),
force: bool = False,
tol: float = 0.2,
) -> NDArray[Shape['*, 2'], Float]:
theta = np.linspace(start=0, stop=2*np.pi, num=nr_points, endpoint=False);
r_min = 0.25;
r_max = 1;
while True:
u = np.random.random(size=(nr_points,));
u_max = max(u);
if u_max == 0.:
continue;
if force:
u = np.minimum((1 + tol) * u / u_max, 1);
else:
u = (1 - tol) * u / u_max;
break;
r = r_min + (r_max - r_min) * u;
P = np.zeros(shape=(nr_points, 2), dtype=float);
P[:, 0] = centre[0] + scale[0] * r * np.cos(theta);
P[:, 1] = centre[1] + scale[1] * r * np.sin(theta);
return P;

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.maths.sets.random import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'random_function',
'randomset_alphabet',
'randomset_greek',
'randomset_integers',
];

62
src/maths/sets/random.py Normal file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations;
from src.thirdparty.types import *;
from src.thirdparty.maths import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'randomset_integers',
'randomset_alphabet',
'randomset_greek',
'random_function',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CONSTANTS / VARIABLES
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ALPHA = 'abcdefghijklmnopqrstuvwxyz';
GREEK = 'αβγδεζηθικλμνξοπρςτυφχψω';
T1 = TypeVar('T1');
T2 = TypeVar('T2');
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHODS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def randomset_integers(low: int, high: int) -> list[int]:
N = random.randint(low, high);
return list(range(1, N+1));
def randomset_alphabet(low: int, high: int) -> list[int]:
N = random.randint(low, high);
return list([a for k, a in enumerate(ALPHA) if k < N]);
def randomset_greek(low: int, high: int) -> list[int]:
N = random.randint(low, high);
return list([a for k, a in enumerate(GREEK) if k < N]);
def random_function(
X: list[T1],
Y: list[T2],
injective: Optional[bool] = None,
surjective: Optional[bool] = None,
) -> list[tuple[T1, T2]]:
# TODO: add feature to force injectivity/surjectivity, if possible.
# m = len(X);
# n = len(Y);
# if m > n:
# injective = False;
# if m < n:
# surjective = False;
return [ (x, random.choice(Y)) for x in X ];

View File

@ -7,9 +7,62 @@
from datetime import datetime;
from datetime import timedelta;
from functools import wraps;
import lorem;
import re;
from textwrap import dedent;
from textwrap import dedent as textwrap_dedent;
from typing import Callable;
from typing import TypeVar;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# MODIFICATIONS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def prestrip(first: bool = True, last: bool = True, all: bool = False):
'''
Returns a decorator that modifies string -> string methods
'''
T = TypeVar('T');
def dec(method: Callable[[str], T]) -> Callable[[str], T]:
'''
Performs method but first strips initial/final (empty) lines.
'''
@wraps(method)
def wrapped_method(text: str) -> T:
lines = re.split(pattern=r'\n', string=text);
if all:
if first:
while len(lines) > 0 and lines[0].strip() == '':
lines = lines[1:];
if last:
while len(lines) > 0 and lines[-1].strip() == '':
lines = lines[:-1];
else:
if first:
lines = lines[1:];
if last:
lines = lines[:-1];
text = '\n'.join(lines);
return method(text);
return wrapped_method;
return dec;
@prestrip(all=False)
def dedent(text: str) -> str:
'''
Remove any common leading whitespace from every line in `text`.
This can be used to make triple-quoted strings line up with the left
edge of the display, while still presenting them in the source code
in indented form.
Note that tabs and spaces are both treated as whitespace, but they
are not equal: the lines " hello" and "\\thello" are
considered to have no common leading whitespace.
Entirely blank lines are normalized to a newline character.
'''
return textwrap_dedent(text);
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS

24
src/thirdparty/plots.py vendored Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from matplotlib import pyplot as mplot;
from matplotlib import colors as mcolours;
from matplotlib.figure import Figure;
from matplotlib.axes import Axes;
from matplotlib.patches import FancyArrowPatch;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'mplot',
'mcolours',
'Figure',
'Axes',
'FancyArrowPatch',
];