master > master: src
This commit is contained in:
parent
ef0265c86d
commit
5e307eb355
0
src/maths/__init__.py
Normal file
0
src/maths/__init__.py
Normal file
17
src/maths/diagrams/__init__.py
Normal file
17
src/maths/diagrams/__init__.py
Normal 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
205
src/maths/diagrams/sets.py
Normal 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;
|
19
src/maths/sets/__init__.py
Normal file
19
src/maths/sets/__init__.py
Normal 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
62
src/maths/sets/random.py
Normal 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 ];
|
55
src/thirdparty/misc.py
vendored
55
src/thirdparty/misc.py
vendored
@ -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
24
src/thirdparty/plots.py
vendored
Normal 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',
|
||||
];
|
Loading…
x
Reference in New Issue
Block a user