Add files using upload-large-folder tool
Browse files- lib/python3.12/site-packages/sympy/benchmarks/__init__.py +0 -0
- lib/python3.12/site-packages/sympy/benchmarks/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/benchmarks/__pycache__/bench_discrete_log.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/benchmarks/__pycache__/bench_meijerint.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/benchmarks/__pycache__/bench_symbench.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/benchmarks/bench_discrete_log.py +83 -0
- lib/python3.12/site-packages/sympy/benchmarks/bench_meijerint.py +261 -0
- lib/python3.12/site-packages/sympy/benchmarks/bench_symbench.py +134 -0
- lib/python3.12/site-packages/sympy/categories/__init__.py +33 -0
- lib/python3.12/site-packages/sympy/categories/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/categories/__pycache__/baseclasses.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/categories/__pycache__/diagram_drawing.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/categories/baseclasses.py +978 -0
- lib/python3.12/site-packages/sympy/categories/diagram_drawing.py +2582 -0
- lib/python3.12/site-packages/sympy/categories/tests/__init__.py +0 -0
- lib/python3.12/site-packages/sympy/categories/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/categories/tests/__pycache__/test_baseclasses.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/categories/tests/__pycache__/test_drawing.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/categories/tests/test_baseclasses.py +209 -0
- lib/python3.12/site-packages/sympy/categories/tests/test_drawing.py +919 -0
- lib/python3.12/site-packages/sympy/liealgebras/__init__.py +3 -0
- lib/python3.12/site-packages/sympy/liealgebras/cartan_matrix.py +25 -0
- lib/python3.12/site-packages/sympy/liealgebras/cartan_type.py +73 -0
- lib/python3.12/site-packages/sympy/liealgebras/dynkin_diagram.py +24 -0
- lib/python3.12/site-packages/sympy/liealgebras/root_system.py +199 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/__init__.py +0 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/__pycache__/test_weyl_group.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_cartan_matrix.py +10 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_cartan_type.py +12 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_dynkin_diagram.py +9 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_root_system.py +18 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_A.py +17 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_B.py +17 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_C.py +22 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_D.py +19 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_E.py +19 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_F.py +24 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_G.py +16 -0
- lib/python3.12/site-packages/sympy/liealgebras/tests/test_weyl_group.py +35 -0
- lib/python3.12/site-packages/sympy/liealgebras/type_a.py +166 -0
- lib/python3.12/site-packages/sympy/liealgebras/type_b.py +172 -0
- lib/python3.12/site-packages/sympy/liealgebras/type_c.py +171 -0
- lib/python3.12/site-packages/sympy/liealgebras/type_d.py +175 -0
- lib/python3.12/site-packages/sympy/liealgebras/type_e.py +287 -0
- lib/python3.12/site-packages/sympy/liealgebras/type_f.py +162 -0
- lib/python3.12/site-packages/sympy/liealgebras/type_g.py +111 -0
- lib/python3.12/site-packages/sympy/liealgebras/weyl_group.py +403 -0
- lib/python3.12/site-packages/sympy/simplify/__pycache__/cse_main.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/simplify/__pycache__/epathtools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/simplify/__pycache__/fu.cpython-312.pyc +0 -0
lib/python3.12/site-packages/sympy/benchmarks/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/sympy/benchmarks/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (195 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/benchmarks/__pycache__/bench_discrete_log.cpython-312.pyc
ADDED
|
Binary file (2.56 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/benchmarks/__pycache__/bench_meijerint.cpython-312.pyc
ADDED
|
Binary file (12.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/benchmarks/__pycache__/bench_symbench.cpython-312.pyc
ADDED
|
Binary file (7.03 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/benchmarks/bench_discrete_log.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from time import time
|
| 3 |
+
from sympy.ntheory.residue_ntheory import (discrete_log,
|
| 4 |
+
_discrete_log_trial_mul, _discrete_log_shanks_steps,
|
| 5 |
+
_discrete_log_pollard_rho, _discrete_log_pohlig_hellman)
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
# Cyclic group (Z/pZ)* with p prime, order p - 1 and generator g
|
| 9 |
+
data_set_1 = [
|
| 10 |
+
# p, p - 1, g
|
| 11 |
+
[191, 190, 19],
|
| 12 |
+
[46639, 46638, 6],
|
| 13 |
+
[14789363, 14789362, 2],
|
| 14 |
+
[4254225211, 4254225210, 2],
|
| 15 |
+
[432751500361, 432751500360, 7],
|
| 16 |
+
[158505390797053, 158505390797052, 2],
|
| 17 |
+
[6575202655312007, 6575202655312006, 5],
|
| 18 |
+
[8430573471995353769, 8430573471995353768, 3],
|
| 19 |
+
[3938471339744997827267, 3938471339744997827266, 2],
|
| 20 |
+
[875260951364705563393093, 875260951364705563393092, 5],
|
| 21 |
+
]
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Cyclic sub-groups of (Z/nZ)* with prime order p and generator g
|
| 25 |
+
# (n, p are primes and n = 2 * p + 1)
|
| 26 |
+
data_set_2 = [
|
| 27 |
+
# n, p, g
|
| 28 |
+
[227, 113, 3],
|
| 29 |
+
[2447, 1223, 2],
|
| 30 |
+
[24527, 12263, 2],
|
| 31 |
+
[245639, 122819, 2],
|
| 32 |
+
[2456747, 1228373, 3],
|
| 33 |
+
[24567899, 12283949, 3],
|
| 34 |
+
[245679023, 122839511, 2],
|
| 35 |
+
[2456791307, 1228395653, 3],
|
| 36 |
+
[24567913439, 12283956719, 2],
|
| 37 |
+
[245679135407, 122839567703, 2],
|
| 38 |
+
[2456791354763, 1228395677381, 3],
|
| 39 |
+
[24567913550903, 12283956775451, 2],
|
| 40 |
+
[245679135509519, 122839567754759, 2],
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# Cyclic sub-groups of (Z/nZ)* with smooth order o and generator g
|
| 45 |
+
data_set_3 = [
|
| 46 |
+
# n, o, g
|
| 47 |
+
[2**118, 2**116, 3],
|
| 48 |
+
]
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def bench_discrete_log(data_set, algo=None):
|
| 52 |
+
if algo is None:
|
| 53 |
+
f = discrete_log
|
| 54 |
+
elif algo == 'trial':
|
| 55 |
+
f = _discrete_log_trial_mul
|
| 56 |
+
elif algo == 'shanks':
|
| 57 |
+
f = _discrete_log_shanks_steps
|
| 58 |
+
elif algo == 'rho':
|
| 59 |
+
f = _discrete_log_pollard_rho
|
| 60 |
+
elif algo == 'ph':
|
| 61 |
+
f = _discrete_log_pohlig_hellman
|
| 62 |
+
else:
|
| 63 |
+
raise ValueError("Argument 'algo' should be one"
|
| 64 |
+
" of ('trial', 'shanks', 'rho' or 'ph')")
|
| 65 |
+
|
| 66 |
+
for i, data in enumerate(data_set):
|
| 67 |
+
for j, (n, p, g) in enumerate(data):
|
| 68 |
+
t = time()
|
| 69 |
+
l = f(n, pow(g, p - 1, n), g, p)
|
| 70 |
+
t = time() - t
|
| 71 |
+
print('[%02d-%03d] %15.10f' % (i, j, t))
|
| 72 |
+
assert l == p - 1
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
if __name__ == '__main__':
|
| 76 |
+
algo = sys.argv[1] \
|
| 77 |
+
if len(sys.argv) > 1 else None
|
| 78 |
+
data_set = [
|
| 79 |
+
data_set_1,
|
| 80 |
+
data_set_2,
|
| 81 |
+
data_set_3,
|
| 82 |
+
]
|
| 83 |
+
bench_discrete_log(data_set, algo)
|
lib/python3.12/site-packages/sympy/benchmarks/bench_meijerint.py
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# conceal the implicit import from the code quality tester
|
| 2 |
+
from sympy.core.numbers import (oo, pi)
|
| 3 |
+
from sympy.core.symbol import (Symbol, symbols)
|
| 4 |
+
from sympy.functions.elementary.exponential import exp
|
| 5 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 6 |
+
from sympy.functions.special.bessel import besseli
|
| 7 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 8 |
+
from sympy.integrals.integrals import integrate
|
| 9 |
+
from sympy.integrals.transforms import (mellin_transform,
|
| 10 |
+
inverse_fourier_transform, inverse_mellin_transform,
|
| 11 |
+
laplace_transform, inverse_laplace_transform, fourier_transform)
|
| 12 |
+
|
| 13 |
+
LT = laplace_transform
|
| 14 |
+
FT = fourier_transform
|
| 15 |
+
MT = mellin_transform
|
| 16 |
+
IFT = inverse_fourier_transform
|
| 17 |
+
ILT = inverse_laplace_transform
|
| 18 |
+
IMT = inverse_mellin_transform
|
| 19 |
+
|
| 20 |
+
from sympy.abc import x, y
|
| 21 |
+
nu, beta, rho = symbols('nu beta rho')
|
| 22 |
+
|
| 23 |
+
apos, bpos, cpos, dpos, posk, p = symbols('a b c d k p', positive=True)
|
| 24 |
+
k = Symbol('k', real=True)
|
| 25 |
+
negk = Symbol('k', negative=True)
|
| 26 |
+
|
| 27 |
+
mu1, mu2 = symbols('mu1 mu2', real=True, nonzero=True, finite=True)
|
| 28 |
+
sigma1, sigma2 = symbols('sigma1 sigma2', real=True, nonzero=True,
|
| 29 |
+
finite=True, positive=True)
|
| 30 |
+
rate = Symbol('lambda', positive=True)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def normal(x, mu, sigma):
|
| 34 |
+
return 1/sqrt(2*pi*sigma**2)*exp(-(x - mu)**2/2/sigma**2)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def exponential(x, rate):
|
| 38 |
+
return rate*exp(-rate*x)
|
| 39 |
+
alpha, beta = symbols('alpha beta', positive=True)
|
| 40 |
+
betadist = x**(alpha - 1)*(1 + x)**(-alpha - beta)*gamma(alpha + beta) \
|
| 41 |
+
/gamma(alpha)/gamma(beta)
|
| 42 |
+
kint = Symbol('k', integer=True, positive=True)
|
| 43 |
+
chi = 2**(1 - kint/2)*x**(kint - 1)*exp(-x**2/2)/gamma(kint/2)
|
| 44 |
+
chisquared = 2**(-k/2)/gamma(k/2)*x**(k/2 - 1)*exp(-x/2)
|
| 45 |
+
dagum = apos*p/x*(x/bpos)**(apos*p)/(1 + x**apos/bpos**apos)**(p + 1)
|
| 46 |
+
d1, d2 = symbols('d1 d2', positive=True)
|
| 47 |
+
f = sqrt(((d1*x)**d1 * d2**d2)/(d1*x + d2)**(d1 + d2))/x \
|
| 48 |
+
/gamma(d1/2)/gamma(d2/2)*gamma((d1 + d2)/2)
|
| 49 |
+
nupos, sigmapos = symbols('nu sigma', positive=True)
|
| 50 |
+
rice = x/sigmapos**2*exp(-(x**2 + nupos**2)/2/sigmapos**2)*besseli(0, x*
|
| 51 |
+
nupos/sigmapos**2)
|
| 52 |
+
mu = Symbol('mu', real=True)
|
| 53 |
+
laplace = exp(-abs(x - mu)/bpos)/2/bpos
|
| 54 |
+
|
| 55 |
+
u = Symbol('u', polar=True)
|
| 56 |
+
tpos = Symbol('t', positive=True)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def E(expr):
|
| 60 |
+
integrate(expr*exponential(x, rate)*normal(y, mu1, sigma1),
|
| 61 |
+
(x, 0, oo), (y, -oo, oo), meijerg=True)
|
| 62 |
+
integrate(expr*exponential(x, rate)*normal(y, mu1, sigma1),
|
| 63 |
+
(y, -oo, oo), (x, 0, oo), meijerg=True)
|
| 64 |
+
|
| 65 |
+
bench = [
|
| 66 |
+
'MT(x**nu*Heaviside(x - 1), x, s)',
|
| 67 |
+
'MT(x**nu*Heaviside(1 - x), x, s)',
|
| 68 |
+
'MT((1-x)**(beta - 1)*Heaviside(1-x), x, s)',
|
| 69 |
+
'MT((x-1)**(beta - 1)*Heaviside(x-1), x, s)',
|
| 70 |
+
'MT((1+x)**(-rho), x, s)',
|
| 71 |
+
'MT(abs(1-x)**(-rho), x, s)',
|
| 72 |
+
'MT((1-x)**(beta-1)*Heaviside(1-x) + a*(x-1)**(beta-1)*Heaviside(x-1), x, s)',
|
| 73 |
+
'MT((x**a-b**a)/(x-b), x, s)',
|
| 74 |
+
'MT((x**a-bpos**a)/(x-bpos), x, s)',
|
| 75 |
+
'MT(exp(-x), x, s)',
|
| 76 |
+
'MT(exp(-1/x), x, s)',
|
| 77 |
+
'MT(log(x)**4*Heaviside(1-x), x, s)',
|
| 78 |
+
'MT(log(x)**3*Heaviside(x-1), x, s)',
|
| 79 |
+
'MT(log(x + 1), x, s)',
|
| 80 |
+
'MT(log(1/x + 1), x, s)',
|
| 81 |
+
'MT(log(abs(1 - x)), x, s)',
|
| 82 |
+
'MT(log(abs(1 - 1/x)), x, s)',
|
| 83 |
+
'MT(log(x)/(x+1), x, s)',
|
| 84 |
+
'MT(log(x)**2/(x+1), x, s)',
|
| 85 |
+
'MT(log(x)/(x+1)**2, x, s)',
|
| 86 |
+
'MT(erf(sqrt(x)), x, s)',
|
| 87 |
+
|
| 88 |
+
'MT(besselj(a, 2*sqrt(x)), x, s)',
|
| 89 |
+
'MT(sin(sqrt(x))*besselj(a, sqrt(x)), x, s)',
|
| 90 |
+
'MT(cos(sqrt(x))*besselj(a, sqrt(x)), x, s)',
|
| 91 |
+
'MT(besselj(a, sqrt(x))**2, x, s)',
|
| 92 |
+
'MT(besselj(a, sqrt(x))*besselj(-a, sqrt(x)), x, s)',
|
| 93 |
+
'MT(besselj(a - 1, sqrt(x))*besselj(a, sqrt(x)), x, s)',
|
| 94 |
+
'MT(besselj(a, sqrt(x))*besselj(b, sqrt(x)), x, s)',
|
| 95 |
+
'MT(besselj(a, sqrt(x))**2 + besselj(-a, sqrt(x))**2, x, s)',
|
| 96 |
+
'MT(bessely(a, 2*sqrt(x)), x, s)',
|
| 97 |
+
'MT(sin(sqrt(x))*bessely(a, sqrt(x)), x, s)',
|
| 98 |
+
'MT(cos(sqrt(x))*bessely(a, sqrt(x)), x, s)',
|
| 99 |
+
'MT(besselj(a, sqrt(x))*bessely(a, sqrt(x)), x, s)',
|
| 100 |
+
'MT(besselj(a, sqrt(x))*bessely(b, sqrt(x)), x, s)',
|
| 101 |
+
'MT(bessely(a, sqrt(x))**2, x, s)',
|
| 102 |
+
|
| 103 |
+
'MT(besselk(a, 2*sqrt(x)), x, s)',
|
| 104 |
+
'MT(besselj(a, 2*sqrt(2*sqrt(x)))*besselk(a, 2*sqrt(2*sqrt(x))), x, s)',
|
| 105 |
+
'MT(besseli(a, sqrt(x))*besselk(a, sqrt(x)), x, s)',
|
| 106 |
+
'MT(besseli(b, sqrt(x))*besselk(a, sqrt(x)), x, s)',
|
| 107 |
+
'MT(exp(-x/2)*besselk(a, x/2), x, s)',
|
| 108 |
+
|
| 109 |
+
# later: ILT, IMT
|
| 110 |
+
|
| 111 |
+
'LT((t-apos)**bpos*exp(-cpos*(t-apos))*Heaviside(t-apos), t, s)',
|
| 112 |
+
'LT(t**apos, t, s)',
|
| 113 |
+
'LT(Heaviside(t), t, s)',
|
| 114 |
+
'LT(Heaviside(t - apos), t, s)',
|
| 115 |
+
'LT(1 - exp(-apos*t), t, s)',
|
| 116 |
+
'LT((exp(2*t)-1)*exp(-bpos - t)*Heaviside(t)/2, t, s, noconds=True)',
|
| 117 |
+
'LT(exp(t), t, s)',
|
| 118 |
+
'LT(exp(2*t), t, s)',
|
| 119 |
+
'LT(exp(apos*t), t, s)',
|
| 120 |
+
'LT(log(t/apos), t, s)',
|
| 121 |
+
'LT(erf(t), t, s)',
|
| 122 |
+
'LT(sin(apos*t), t, s)',
|
| 123 |
+
'LT(cos(apos*t), t, s)',
|
| 124 |
+
'LT(exp(-apos*t)*sin(bpos*t), t, s)',
|
| 125 |
+
'LT(exp(-apos*t)*cos(bpos*t), t, s)',
|
| 126 |
+
'LT(besselj(0, t), t, s, noconds=True)',
|
| 127 |
+
'LT(besselj(1, t), t, s, noconds=True)',
|
| 128 |
+
|
| 129 |
+
'FT(Heaviside(1 - abs(2*apos*x)), x, k)',
|
| 130 |
+
'FT(Heaviside(1-abs(apos*x))*(1-abs(apos*x)), x, k)',
|
| 131 |
+
'FT(exp(-apos*x)*Heaviside(x), x, k)',
|
| 132 |
+
'IFT(1/(apos + 2*pi*I*x), x, posk, noconds=False)',
|
| 133 |
+
'IFT(1/(apos + 2*pi*I*x), x, -posk, noconds=False)',
|
| 134 |
+
'IFT(1/(apos + 2*pi*I*x), x, negk)',
|
| 135 |
+
'FT(x*exp(-apos*x)*Heaviside(x), x, k)',
|
| 136 |
+
'FT(exp(-apos*x)*sin(bpos*x)*Heaviside(x), x, k)',
|
| 137 |
+
'FT(exp(-apos*x**2), x, k)',
|
| 138 |
+
'IFT(sqrt(pi/apos)*exp(-(pi*k)**2/apos), k, x)',
|
| 139 |
+
'FT(exp(-apos*abs(x)), x, k)',
|
| 140 |
+
|
| 141 |
+
'integrate(normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)',
|
| 142 |
+
'integrate(x*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)',
|
| 143 |
+
'integrate(x**2*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)',
|
| 144 |
+
'integrate(x**3*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)',
|
| 145 |
+
'integrate(normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 146 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 147 |
+
'integrate(x*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 148 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 149 |
+
'integrate(y*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 150 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 151 |
+
'integrate(x*y*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 152 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 153 |
+
'integrate((x+y+1)*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 154 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 155 |
+
'integrate((x+y-1)*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 156 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 157 |
+
'integrate(x**2*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 158 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 159 |
+
'integrate(y**2*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),'
|
| 160 |
+
' (x, -oo, oo), (y, -oo, oo), meijerg=True)',
|
| 161 |
+
'integrate(exponential(x, rate), (x, 0, oo), meijerg=True)',
|
| 162 |
+
'integrate(x*exponential(x, rate), (x, 0, oo), meijerg=True)',
|
| 163 |
+
'integrate(x**2*exponential(x, rate), (x, 0, oo), meijerg=True)',
|
| 164 |
+
'E(1)',
|
| 165 |
+
'E(x*y)',
|
| 166 |
+
'E(x*y**2)',
|
| 167 |
+
'E((x+y+1)**2)',
|
| 168 |
+
'E(x+y+1)',
|
| 169 |
+
'E((x+y-1)**2)',
|
| 170 |
+
'integrate(betadist, (x, 0, oo), meijerg=True)',
|
| 171 |
+
'integrate(x*betadist, (x, 0, oo), meijerg=True)',
|
| 172 |
+
'integrate(x**2*betadist, (x, 0, oo), meijerg=True)',
|
| 173 |
+
'integrate(chi, (x, 0, oo), meijerg=True)',
|
| 174 |
+
'integrate(x*chi, (x, 0, oo), meijerg=True)',
|
| 175 |
+
'integrate(x**2*chi, (x, 0, oo), meijerg=True)',
|
| 176 |
+
'integrate(chisquared, (x, 0, oo), meijerg=True)',
|
| 177 |
+
'integrate(x*chisquared, (x, 0, oo), meijerg=True)',
|
| 178 |
+
'integrate(x**2*chisquared, (x, 0, oo), meijerg=True)',
|
| 179 |
+
'integrate(((x-k)/sqrt(2*k))**3*chisquared, (x, 0, oo), meijerg=True)',
|
| 180 |
+
'integrate(dagum, (x, 0, oo), meijerg=True)',
|
| 181 |
+
'integrate(x*dagum, (x, 0, oo), meijerg=True)',
|
| 182 |
+
'integrate(x**2*dagum, (x, 0, oo), meijerg=True)',
|
| 183 |
+
'integrate(f, (x, 0, oo), meijerg=True)',
|
| 184 |
+
'integrate(x*f, (x, 0, oo), meijerg=True)',
|
| 185 |
+
'integrate(x**2*f, (x, 0, oo), meijerg=True)',
|
| 186 |
+
'integrate(rice, (x, 0, oo), meijerg=True)',
|
| 187 |
+
'integrate(laplace, (x, -oo, oo), meijerg=True)',
|
| 188 |
+
'integrate(x*laplace, (x, -oo, oo), meijerg=True)',
|
| 189 |
+
'integrate(x**2*laplace, (x, -oo, oo), meijerg=True)',
|
| 190 |
+
'integrate(log(x) * x**(k-1) * exp(-x) / gamma(k), (x, 0, oo))',
|
| 191 |
+
|
| 192 |
+
'integrate(sin(z*x)*(x**2-1)**(-(y+S(1)/2)), (x, 1, oo), meijerg=True)',
|
| 193 |
+
'integrate(besselj(0,x)*besselj(1,x)*exp(-x**2), (x, 0, oo), meijerg=True)',
|
| 194 |
+
'integrate(besselj(0,x)*besselj(1,x)*besselk(0,x), (x, 0, oo), meijerg=True)',
|
| 195 |
+
'integrate(besselj(0,x)*besselj(1,x)*exp(-x**2), (x, 0, oo), meijerg=True)',
|
| 196 |
+
'integrate(besselj(a,x)*besselj(b,x)/x, (x,0,oo), meijerg=True)',
|
| 197 |
+
|
| 198 |
+
'hyperexpand(meijerg((-s - a/2 + 1, -s + a/2 + 1), (-a/2 - S(1)/2, -s + a/2 + S(3)/2), (a/2, -a/2), (-a/2 - S(1)/2, -s + a/2 + S(3)/2), 1))',
|
| 199 |
+
"gammasimp(S('2**(2*s)*(-pi*gamma(-a + 1)*gamma(a + 1)*gamma(-a - s + 1)*gamma(-a + s - 1/2)*gamma(a - s + 3/2)*gamma(a + s + 1)/(a*(a + s)) - gamma(-a - 1/2)*gamma(-a + 1)*gamma(a + 1)*gamma(a + 3/2)*gamma(-s + 3/2)*gamma(s - 1/2)*gamma(-a + s + 1)*gamma(a - s + 1)/(a*(-a + s)))*gamma(-2*s + 1)*gamma(s + 1)/(pi*s*gamma(-a - 1/2)*gamma(a + 3/2)*gamma(-s + 1)*gamma(-s + 3/2)*gamma(s - 1/2)*gamma(-a - s + 1)*gamma(-a + s - 1/2)*gamma(a - s + 1)*gamma(a - s + 3/2))'))",
|
| 200 |
+
|
| 201 |
+
'mellin_transform(E1(x), x, s)',
|
| 202 |
+
'inverse_mellin_transform(gamma(s)/s, s, x, (0, oo))',
|
| 203 |
+
'mellin_transform(expint(a, x), x, s)',
|
| 204 |
+
'mellin_transform(Si(x), x, s)',
|
| 205 |
+
'inverse_mellin_transform(-2**s*sqrt(pi)*gamma((s + 1)/2)/(2*s*gamma(-s/2 + 1)), s, x, (-1, 0))',
|
| 206 |
+
'mellin_transform(Ci(sqrt(x)), x, s)',
|
| 207 |
+
'inverse_mellin_transform(-4**s*sqrt(pi)*gamma(s)/(2*s*gamma(-s + S(1)/2)),s, u, (0, 1))',
|
| 208 |
+
'laplace_transform(Ci(x), x, s)',
|
| 209 |
+
'laplace_transform(expint(a, x), x, s)',
|
| 210 |
+
'laplace_transform(expint(1, x), x, s)',
|
| 211 |
+
'laplace_transform(expint(2, x), x, s)',
|
| 212 |
+
'inverse_laplace_transform(-log(1 + s**2)/2/s, s, u)',
|
| 213 |
+
'inverse_laplace_transform(log(s + 1)/s, s, x)',
|
| 214 |
+
'inverse_laplace_transform((s - log(s + 1))/s**2, s, x)',
|
| 215 |
+
'laplace_transform(Chi(x), x, s)',
|
| 216 |
+
'laplace_transform(Shi(x), x, s)',
|
| 217 |
+
|
| 218 |
+
'integrate(exp(-z*x)/x, (x, 1, oo), meijerg=True, conds="none")',
|
| 219 |
+
'integrate(exp(-z*x)/x**2, (x, 1, oo), meijerg=True, conds="none")',
|
| 220 |
+
'integrate(exp(-z*x)/x**3, (x, 1, oo), meijerg=True,conds="none")',
|
| 221 |
+
'integrate(-cos(x)/x, (x, tpos, oo), meijerg=True)',
|
| 222 |
+
'integrate(-sin(x)/x, (x, tpos, oo), meijerg=True)',
|
| 223 |
+
'integrate(sin(x)/x, (x, 0, z), meijerg=True)',
|
| 224 |
+
'integrate(sinh(x)/x, (x, 0, z), meijerg=True)',
|
| 225 |
+
'integrate(exp(-x)/x, x, meijerg=True)',
|
| 226 |
+
'integrate(exp(-x)/x**2, x, meijerg=True)',
|
| 227 |
+
'integrate(cos(u)/u, u, meijerg=True)',
|
| 228 |
+
'integrate(cosh(u)/u, u, meijerg=True)',
|
| 229 |
+
'integrate(expint(1, x), x, meijerg=True)',
|
| 230 |
+
'integrate(expint(2, x), x, meijerg=True)',
|
| 231 |
+
'integrate(Si(x), x, meijerg=True)',
|
| 232 |
+
'integrate(Ci(u), u, meijerg=True)',
|
| 233 |
+
'integrate(Shi(x), x, meijerg=True)',
|
| 234 |
+
'integrate(Chi(u), u, meijerg=True)',
|
| 235 |
+
'integrate(Si(x)*exp(-x), (x, 0, oo), meijerg=True)',
|
| 236 |
+
'integrate(expint(1, x)*sin(x), (x, 0, oo), meijerg=True)'
|
| 237 |
+
]
|
| 238 |
+
|
| 239 |
+
from time import time
|
| 240 |
+
from sympy.core.cache import clear_cache
|
| 241 |
+
import sys
|
| 242 |
+
|
| 243 |
+
timings = []
|
| 244 |
+
|
| 245 |
+
if __name__ == '__main__':
|
| 246 |
+
for n, string in enumerate(bench):
|
| 247 |
+
clear_cache()
|
| 248 |
+
_t = time()
|
| 249 |
+
exec(string)
|
| 250 |
+
_t = time() - _t
|
| 251 |
+
timings += [(_t, string)]
|
| 252 |
+
sys.stdout.write('.')
|
| 253 |
+
sys.stdout.flush()
|
| 254 |
+
if n % (len(bench) // 10) == 0:
|
| 255 |
+
sys.stdout.write('%s' % (10*n // len(bench)))
|
| 256 |
+
print()
|
| 257 |
+
|
| 258 |
+
timings.sort(key=lambda x: -x[0])
|
| 259 |
+
|
| 260 |
+
for ti, string in timings:
|
| 261 |
+
print('%.2fs %s' % (ti, string))
|
lib/python3.12/site-packages/sympy/benchmarks/bench_symbench.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
from sympy.core.random import random
|
| 3 |
+
from sympy.core.numbers import (I, Integer, pi)
|
| 4 |
+
from sympy.core.symbol import Symbol
|
| 5 |
+
from sympy.core.sympify import sympify
|
| 6 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 7 |
+
from sympy.functions.elementary.trigonometric import sin
|
| 8 |
+
from sympy.polys.polytools import factor
|
| 9 |
+
from sympy.simplify.simplify import simplify
|
| 10 |
+
from sympy.abc import x, y, z
|
| 11 |
+
from timeit import default_timer as clock
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def bench_R1():
|
| 15 |
+
"real(f(f(f(f(f(f(f(f(f(f(i/2)))))))))))"
|
| 16 |
+
def f(z):
|
| 17 |
+
return sqrt(Integer(1)/3)*z**2 + I/3
|
| 18 |
+
f(f(f(f(f(f(f(f(f(f(I/2)))))))))).as_real_imag()[0]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def bench_R2():
|
| 22 |
+
"Hermite polynomial hermite(15, y)"
|
| 23 |
+
def hermite(n, y):
|
| 24 |
+
if n == 1:
|
| 25 |
+
return 2*y
|
| 26 |
+
if n == 0:
|
| 27 |
+
return 1
|
| 28 |
+
return (2*y*hermite(n - 1, y) - 2*(n - 1)*hermite(n - 2, y)).expand()
|
| 29 |
+
|
| 30 |
+
hermite(15, y)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def bench_R3():
|
| 34 |
+
"a = [bool(f==f) for _ in range(10)]"
|
| 35 |
+
f = x + y + z
|
| 36 |
+
[bool(f == f) for _ in range(10)]
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def bench_R4():
|
| 40 |
+
# we don't have Tuples
|
| 41 |
+
pass
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def bench_R5():
|
| 45 |
+
"blowup(L, 8); L=uniq(L)"
|
| 46 |
+
def blowup(L, n):
|
| 47 |
+
for i in range(n):
|
| 48 |
+
L.append( (L[i] + L[i + 1]) * L[i + 2] )
|
| 49 |
+
|
| 50 |
+
def uniq(x):
|
| 51 |
+
v = set(x)
|
| 52 |
+
return v
|
| 53 |
+
L = [x, y, z]
|
| 54 |
+
blowup(L, 8)
|
| 55 |
+
L = uniq(L)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def bench_R6():
|
| 59 |
+
"sum(simplify((x+sin(i))/x+(x-sin(i))/x) for i in range(100))"
|
| 60 |
+
sum(simplify((x + sin(i))/x + (x - sin(i))/x) for i in range(100))
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def bench_R7():
|
| 64 |
+
"[f.subs(x, random()) for _ in range(10**4)]"
|
| 65 |
+
f = x**24 + 34*x**12 + 45*x**3 + 9*x**18 + 34*x**10 + 32*x**21
|
| 66 |
+
[f.subs(x, random()) for _ in range(10**4)]
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def bench_R8():
|
| 70 |
+
"right(x^2,0,5,10^4)"
|
| 71 |
+
def right(f, a, b, n):
|
| 72 |
+
a = sympify(a)
|
| 73 |
+
b = sympify(b)
|
| 74 |
+
n = sympify(n)
|
| 75 |
+
x = f.atoms(Symbol).pop()
|
| 76 |
+
Deltax = (b - a)/n
|
| 77 |
+
c = a
|
| 78 |
+
est = 0
|
| 79 |
+
for i in range(n):
|
| 80 |
+
c += Deltax
|
| 81 |
+
est += f.subs(x, c)
|
| 82 |
+
return est*Deltax
|
| 83 |
+
|
| 84 |
+
right(x**2, 0, 5, 10**4)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _bench_R9():
|
| 88 |
+
"factor(x^20 - pi^5*y^20)"
|
| 89 |
+
factor(x**20 - pi**5*y**20)
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def bench_R10():
|
| 93 |
+
"v = [-pi,-pi+1/10..,pi]"
|
| 94 |
+
def srange(min, max, step):
|
| 95 |
+
v = [min]
|
| 96 |
+
while (max - v[-1]).evalf() > 0:
|
| 97 |
+
v.append(v[-1] + step)
|
| 98 |
+
return v[:-1]
|
| 99 |
+
srange(-pi, pi, sympify(1)/10)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def bench_R11():
|
| 103 |
+
"a = [random() + random()*I for w in [0..1000]]"
|
| 104 |
+
[random() + random()*I for w in range(1000)]
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def bench_S1():
|
| 108 |
+
"e=(x+y+z+1)**7;f=e*(e+1);f.expand()"
|
| 109 |
+
e = (x + y + z + 1)**7
|
| 110 |
+
f = e*(e + 1)
|
| 111 |
+
f.expand()
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
if __name__ == '__main__':
|
| 115 |
+
benchmarks = [
|
| 116 |
+
bench_R1,
|
| 117 |
+
bench_R2,
|
| 118 |
+
bench_R3,
|
| 119 |
+
bench_R5,
|
| 120 |
+
bench_R6,
|
| 121 |
+
bench_R7,
|
| 122 |
+
bench_R8,
|
| 123 |
+
#_bench_R9,
|
| 124 |
+
bench_R10,
|
| 125 |
+
bench_R11,
|
| 126 |
+
#bench_S1,
|
| 127 |
+
]
|
| 128 |
+
|
| 129 |
+
report = []
|
| 130 |
+
for b in benchmarks:
|
| 131 |
+
t = clock()
|
| 132 |
+
b()
|
| 133 |
+
t = clock() - t
|
| 134 |
+
print("%s%65s: %f" % (b.__name__, b.__doc__, t))
|
lib/python3.12/site-packages/sympy/categories/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Category Theory module.
|
| 3 |
+
|
| 4 |
+
Provides some of the fundamental category-theory-related classes,
|
| 5 |
+
including categories, morphisms, diagrams. Functors are not
|
| 6 |
+
implemented yet.
|
| 7 |
+
|
| 8 |
+
The general reference work this module tries to follow is
|
| 9 |
+
|
| 10 |
+
[JoyOfCats] J. Adamek, H. Herrlich. G. E. Strecker: Abstract and
|
| 11 |
+
Concrete Categories. The Joy of Cats.
|
| 12 |
+
|
| 13 |
+
The latest version of this book should be available for free download
|
| 14 |
+
from
|
| 15 |
+
|
| 16 |
+
katmat.math.uni-bremen.de/acc/acc.pdf
|
| 17 |
+
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from .baseclasses import (Object, Morphism, IdentityMorphism,
|
| 21 |
+
NamedMorphism, CompositeMorphism, Category,
|
| 22 |
+
Diagram)
|
| 23 |
+
|
| 24 |
+
from .diagram_drawing import (DiagramGrid, XypicDiagramDrawer,
|
| 25 |
+
xypic_draw_diagram, preview_diagram)
|
| 26 |
+
|
| 27 |
+
__all__ = [
|
| 28 |
+
'Object', 'Morphism', 'IdentityMorphism', 'NamedMorphism',
|
| 29 |
+
'CompositeMorphism', 'Category', 'Diagram',
|
| 30 |
+
|
| 31 |
+
'DiagramGrid', 'XypicDiagramDrawer', 'xypic_draw_diagram',
|
| 32 |
+
'preview_diagram',
|
| 33 |
+
]
|
lib/python3.12/site-packages/sympy/categories/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (1.1 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/categories/__pycache__/baseclasses.cpython-312.pyc
ADDED
|
Binary file (34.5 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/categories/__pycache__/diagram_drawing.cpython-312.pyc
ADDED
|
Binary file (85.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/categories/baseclasses.py
ADDED
|
@@ -0,0 +1,978 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import S, Basic, Dict, Symbol, Tuple, sympify
|
| 2 |
+
from sympy.core.symbol import Str
|
| 3 |
+
from sympy.sets import Set, FiniteSet, EmptySet
|
| 4 |
+
from sympy.utilities.iterables import iterable
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Class(Set):
|
| 8 |
+
r"""
|
| 9 |
+
The base class for any kind of class in the set-theoretic sense.
|
| 10 |
+
|
| 11 |
+
Explanation
|
| 12 |
+
===========
|
| 13 |
+
|
| 14 |
+
In axiomatic set theories, everything is a class. A class which
|
| 15 |
+
can be a member of another class is a set. A class which is not a
|
| 16 |
+
member of another class is a proper class. The class `\{1, 2\}`
|
| 17 |
+
is a set; the class of all sets is a proper class.
|
| 18 |
+
|
| 19 |
+
This class is essentially a synonym for :class:`sympy.core.Set`.
|
| 20 |
+
The goal of this class is to assure easier migration to the
|
| 21 |
+
eventual proper implementation of set theory.
|
| 22 |
+
"""
|
| 23 |
+
is_proper = False
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class Object(Symbol):
|
| 27 |
+
"""
|
| 28 |
+
The base class for any kind of object in an abstract category.
|
| 29 |
+
|
| 30 |
+
Explanation
|
| 31 |
+
===========
|
| 32 |
+
|
| 33 |
+
While technically any instance of :class:`~.Basic` will do, this
|
| 34 |
+
class is the recommended way to create abstract objects in
|
| 35 |
+
abstract categories.
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class Morphism(Basic):
|
| 40 |
+
"""
|
| 41 |
+
The base class for any morphism in an abstract category.
|
| 42 |
+
|
| 43 |
+
Explanation
|
| 44 |
+
===========
|
| 45 |
+
|
| 46 |
+
In abstract categories, a morphism is an arrow between two
|
| 47 |
+
category objects. The object where the arrow starts is called the
|
| 48 |
+
domain, while the object where the arrow ends is called the
|
| 49 |
+
codomain.
|
| 50 |
+
|
| 51 |
+
Two morphisms between the same pair of objects are considered to
|
| 52 |
+
be the same morphisms. To distinguish between morphisms between
|
| 53 |
+
the same objects use :class:`NamedMorphism`.
|
| 54 |
+
|
| 55 |
+
It is prohibited to instantiate this class. Use one of the
|
| 56 |
+
derived classes instead.
|
| 57 |
+
|
| 58 |
+
See Also
|
| 59 |
+
========
|
| 60 |
+
|
| 61 |
+
IdentityMorphism, NamedMorphism, CompositeMorphism
|
| 62 |
+
"""
|
| 63 |
+
def __new__(cls, domain, codomain):
|
| 64 |
+
raise(NotImplementedError(
|
| 65 |
+
"Cannot instantiate Morphism. Use derived classes instead."))
|
| 66 |
+
|
| 67 |
+
@property
|
| 68 |
+
def domain(self):
|
| 69 |
+
"""
|
| 70 |
+
Returns the domain of the morphism.
|
| 71 |
+
|
| 72 |
+
Examples
|
| 73 |
+
========
|
| 74 |
+
|
| 75 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 76 |
+
>>> A = Object("A")
|
| 77 |
+
>>> B = Object("B")
|
| 78 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 79 |
+
>>> f.domain
|
| 80 |
+
Object("A")
|
| 81 |
+
|
| 82 |
+
"""
|
| 83 |
+
return self.args[0]
|
| 84 |
+
|
| 85 |
+
@property
|
| 86 |
+
def codomain(self):
|
| 87 |
+
"""
|
| 88 |
+
Returns the codomain of the morphism.
|
| 89 |
+
|
| 90 |
+
Examples
|
| 91 |
+
========
|
| 92 |
+
|
| 93 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 94 |
+
>>> A = Object("A")
|
| 95 |
+
>>> B = Object("B")
|
| 96 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 97 |
+
>>> f.codomain
|
| 98 |
+
Object("B")
|
| 99 |
+
|
| 100 |
+
"""
|
| 101 |
+
return self.args[1]
|
| 102 |
+
|
| 103 |
+
def compose(self, other):
|
| 104 |
+
r"""
|
| 105 |
+
Composes self with the supplied morphism.
|
| 106 |
+
|
| 107 |
+
The order of elements in the composition is the usual order,
|
| 108 |
+
i.e., to construct `g\circ f` use ``g.compose(f)``.
|
| 109 |
+
|
| 110 |
+
Examples
|
| 111 |
+
========
|
| 112 |
+
|
| 113 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 114 |
+
>>> A = Object("A")
|
| 115 |
+
>>> B = Object("B")
|
| 116 |
+
>>> C = Object("C")
|
| 117 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 118 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 119 |
+
>>> g * f
|
| 120 |
+
CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"),
|
| 121 |
+
NamedMorphism(Object("B"), Object("C"), "g")))
|
| 122 |
+
>>> (g * f).domain
|
| 123 |
+
Object("A")
|
| 124 |
+
>>> (g * f).codomain
|
| 125 |
+
Object("C")
|
| 126 |
+
|
| 127 |
+
"""
|
| 128 |
+
return CompositeMorphism(other, self)
|
| 129 |
+
|
| 130 |
+
def __mul__(self, other):
|
| 131 |
+
r"""
|
| 132 |
+
Composes self with the supplied morphism.
|
| 133 |
+
|
| 134 |
+
The semantics of this operation is given by the following
|
| 135 |
+
equation: ``g * f == g.compose(f)`` for composable morphisms
|
| 136 |
+
``g`` and ``f``.
|
| 137 |
+
|
| 138 |
+
See Also
|
| 139 |
+
========
|
| 140 |
+
|
| 141 |
+
compose
|
| 142 |
+
"""
|
| 143 |
+
return self.compose(other)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
class IdentityMorphism(Morphism):
|
| 147 |
+
"""
|
| 148 |
+
Represents an identity morphism.
|
| 149 |
+
|
| 150 |
+
Explanation
|
| 151 |
+
===========
|
| 152 |
+
|
| 153 |
+
An identity morphism is a morphism with equal domain and codomain,
|
| 154 |
+
which acts as an identity with respect to composition.
|
| 155 |
+
|
| 156 |
+
Examples
|
| 157 |
+
========
|
| 158 |
+
|
| 159 |
+
>>> from sympy.categories import Object, NamedMorphism, IdentityMorphism
|
| 160 |
+
>>> A = Object("A")
|
| 161 |
+
>>> B = Object("B")
|
| 162 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 163 |
+
>>> id_A = IdentityMorphism(A)
|
| 164 |
+
>>> id_B = IdentityMorphism(B)
|
| 165 |
+
>>> f * id_A == f
|
| 166 |
+
True
|
| 167 |
+
>>> id_B * f == f
|
| 168 |
+
True
|
| 169 |
+
|
| 170 |
+
See Also
|
| 171 |
+
========
|
| 172 |
+
|
| 173 |
+
Morphism
|
| 174 |
+
"""
|
| 175 |
+
def __new__(cls, domain):
|
| 176 |
+
return Basic.__new__(cls, domain)
|
| 177 |
+
|
| 178 |
+
@property
|
| 179 |
+
def codomain(self):
|
| 180 |
+
return self.domain
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
class NamedMorphism(Morphism):
|
| 184 |
+
"""
|
| 185 |
+
Represents a morphism which has a name.
|
| 186 |
+
|
| 187 |
+
Explanation
|
| 188 |
+
===========
|
| 189 |
+
|
| 190 |
+
Names are used to distinguish between morphisms which have the
|
| 191 |
+
same domain and codomain: two named morphisms are equal if they
|
| 192 |
+
have the same domains, codomains, and names.
|
| 193 |
+
|
| 194 |
+
Examples
|
| 195 |
+
========
|
| 196 |
+
|
| 197 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 198 |
+
>>> A = Object("A")
|
| 199 |
+
>>> B = Object("B")
|
| 200 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 201 |
+
>>> f
|
| 202 |
+
NamedMorphism(Object("A"), Object("B"), "f")
|
| 203 |
+
>>> f.name
|
| 204 |
+
'f'
|
| 205 |
+
|
| 206 |
+
See Also
|
| 207 |
+
========
|
| 208 |
+
|
| 209 |
+
Morphism
|
| 210 |
+
"""
|
| 211 |
+
def __new__(cls, domain, codomain, name):
|
| 212 |
+
if not name:
|
| 213 |
+
raise ValueError("Empty morphism names not allowed.")
|
| 214 |
+
|
| 215 |
+
if not isinstance(name, Str):
|
| 216 |
+
name = Str(name)
|
| 217 |
+
|
| 218 |
+
return Basic.__new__(cls, domain, codomain, name)
|
| 219 |
+
|
| 220 |
+
@property
|
| 221 |
+
def name(self):
|
| 222 |
+
"""
|
| 223 |
+
Returns the name of the morphism.
|
| 224 |
+
|
| 225 |
+
Examples
|
| 226 |
+
========
|
| 227 |
+
|
| 228 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 229 |
+
>>> A = Object("A")
|
| 230 |
+
>>> B = Object("B")
|
| 231 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 232 |
+
>>> f.name
|
| 233 |
+
'f'
|
| 234 |
+
|
| 235 |
+
"""
|
| 236 |
+
return self.args[2].name
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
class CompositeMorphism(Morphism):
|
| 240 |
+
r"""
|
| 241 |
+
Represents a morphism which is a composition of other morphisms.
|
| 242 |
+
|
| 243 |
+
Explanation
|
| 244 |
+
===========
|
| 245 |
+
|
| 246 |
+
Two composite morphisms are equal if the morphisms they were
|
| 247 |
+
obtained from (components) are the same and were listed in the
|
| 248 |
+
same order.
|
| 249 |
+
|
| 250 |
+
The arguments to the constructor for this class should be listed
|
| 251 |
+
in diagram order: to obtain the composition `g\circ f` from the
|
| 252 |
+
instances of :class:`Morphism` ``g`` and ``f`` use
|
| 253 |
+
``CompositeMorphism(f, g)``.
|
| 254 |
+
|
| 255 |
+
Examples
|
| 256 |
+
========
|
| 257 |
+
|
| 258 |
+
>>> from sympy.categories import Object, NamedMorphism, CompositeMorphism
|
| 259 |
+
>>> A = Object("A")
|
| 260 |
+
>>> B = Object("B")
|
| 261 |
+
>>> C = Object("C")
|
| 262 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 263 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 264 |
+
>>> g * f
|
| 265 |
+
CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"),
|
| 266 |
+
NamedMorphism(Object("B"), Object("C"), "g")))
|
| 267 |
+
>>> CompositeMorphism(f, g) == g * f
|
| 268 |
+
True
|
| 269 |
+
|
| 270 |
+
"""
|
| 271 |
+
@staticmethod
|
| 272 |
+
def _add_morphism(t, morphism):
|
| 273 |
+
"""
|
| 274 |
+
Intelligently adds ``morphism`` to tuple ``t``.
|
| 275 |
+
|
| 276 |
+
Explanation
|
| 277 |
+
===========
|
| 278 |
+
|
| 279 |
+
If ``morphism`` is a composite morphism, its components are
|
| 280 |
+
added to the tuple. If ``morphism`` is an identity, nothing
|
| 281 |
+
is added to the tuple.
|
| 282 |
+
|
| 283 |
+
No composability checks are performed.
|
| 284 |
+
"""
|
| 285 |
+
if isinstance(morphism, CompositeMorphism):
|
| 286 |
+
# ``morphism`` is a composite morphism; we have to
|
| 287 |
+
# denest its components.
|
| 288 |
+
return t + morphism.components
|
| 289 |
+
elif isinstance(morphism, IdentityMorphism):
|
| 290 |
+
# ``morphism`` is an identity. Nothing happens.
|
| 291 |
+
return t
|
| 292 |
+
else:
|
| 293 |
+
return t + Tuple(morphism)
|
| 294 |
+
|
| 295 |
+
def __new__(cls, *components):
|
| 296 |
+
if components and not isinstance(components[0], Morphism):
|
| 297 |
+
# Maybe the user has explicitly supplied a list of
|
| 298 |
+
# morphisms.
|
| 299 |
+
return CompositeMorphism.__new__(cls, *components[0])
|
| 300 |
+
|
| 301 |
+
normalised_components = Tuple()
|
| 302 |
+
|
| 303 |
+
for current, following in zip(components, components[1:]):
|
| 304 |
+
if not isinstance(current, Morphism) or \
|
| 305 |
+
not isinstance(following, Morphism):
|
| 306 |
+
raise TypeError("All components must be morphisms.")
|
| 307 |
+
|
| 308 |
+
if current.codomain != following.domain:
|
| 309 |
+
raise ValueError("Uncomposable morphisms.")
|
| 310 |
+
|
| 311 |
+
normalised_components = CompositeMorphism._add_morphism(
|
| 312 |
+
normalised_components, current)
|
| 313 |
+
|
| 314 |
+
# We haven't added the last morphism to the list of normalised
|
| 315 |
+
# components. Add it now.
|
| 316 |
+
normalised_components = CompositeMorphism._add_morphism(
|
| 317 |
+
normalised_components, components[-1])
|
| 318 |
+
|
| 319 |
+
if not normalised_components:
|
| 320 |
+
# If ``normalised_components`` is empty, only identities
|
| 321 |
+
# were supplied. Since they all were composable, they are
|
| 322 |
+
# all the same identities.
|
| 323 |
+
return components[0]
|
| 324 |
+
elif len(normalised_components) == 1:
|
| 325 |
+
# No sense to construct a whole CompositeMorphism.
|
| 326 |
+
return normalised_components[0]
|
| 327 |
+
|
| 328 |
+
return Basic.__new__(cls, normalised_components)
|
| 329 |
+
|
| 330 |
+
@property
|
| 331 |
+
def components(self):
|
| 332 |
+
"""
|
| 333 |
+
Returns the components of this composite morphism.
|
| 334 |
+
|
| 335 |
+
Examples
|
| 336 |
+
========
|
| 337 |
+
|
| 338 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 339 |
+
>>> A = Object("A")
|
| 340 |
+
>>> B = Object("B")
|
| 341 |
+
>>> C = Object("C")
|
| 342 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 343 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 344 |
+
>>> (g * f).components
|
| 345 |
+
(NamedMorphism(Object("A"), Object("B"), "f"),
|
| 346 |
+
NamedMorphism(Object("B"), Object("C"), "g"))
|
| 347 |
+
|
| 348 |
+
"""
|
| 349 |
+
return self.args[0]
|
| 350 |
+
|
| 351 |
+
@property
|
| 352 |
+
def domain(self):
|
| 353 |
+
"""
|
| 354 |
+
Returns the domain of this composite morphism.
|
| 355 |
+
|
| 356 |
+
The domain of the composite morphism is the domain of its
|
| 357 |
+
first component.
|
| 358 |
+
|
| 359 |
+
Examples
|
| 360 |
+
========
|
| 361 |
+
|
| 362 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 363 |
+
>>> A = Object("A")
|
| 364 |
+
>>> B = Object("B")
|
| 365 |
+
>>> C = Object("C")
|
| 366 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 367 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 368 |
+
>>> (g * f).domain
|
| 369 |
+
Object("A")
|
| 370 |
+
|
| 371 |
+
"""
|
| 372 |
+
return self.components[0].domain
|
| 373 |
+
|
| 374 |
+
@property
|
| 375 |
+
def codomain(self):
|
| 376 |
+
"""
|
| 377 |
+
Returns the codomain of this composite morphism.
|
| 378 |
+
|
| 379 |
+
The codomain of the composite morphism is the codomain of its
|
| 380 |
+
last component.
|
| 381 |
+
|
| 382 |
+
Examples
|
| 383 |
+
========
|
| 384 |
+
|
| 385 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 386 |
+
>>> A = Object("A")
|
| 387 |
+
>>> B = Object("B")
|
| 388 |
+
>>> C = Object("C")
|
| 389 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 390 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 391 |
+
>>> (g * f).codomain
|
| 392 |
+
Object("C")
|
| 393 |
+
|
| 394 |
+
"""
|
| 395 |
+
return self.components[-1].codomain
|
| 396 |
+
|
| 397 |
+
def flatten(self, new_name):
|
| 398 |
+
"""
|
| 399 |
+
Forgets the composite structure of this morphism.
|
| 400 |
+
|
| 401 |
+
Explanation
|
| 402 |
+
===========
|
| 403 |
+
|
| 404 |
+
If ``new_name`` is not empty, returns a :class:`NamedMorphism`
|
| 405 |
+
with the supplied name, otherwise returns a :class:`Morphism`.
|
| 406 |
+
In both cases the domain of the new morphism is the domain of
|
| 407 |
+
this composite morphism and the codomain of the new morphism
|
| 408 |
+
is the codomain of this composite morphism.
|
| 409 |
+
|
| 410 |
+
Examples
|
| 411 |
+
========
|
| 412 |
+
|
| 413 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 414 |
+
>>> A = Object("A")
|
| 415 |
+
>>> B = Object("B")
|
| 416 |
+
>>> C = Object("C")
|
| 417 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 418 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 419 |
+
>>> (g * f).flatten("h")
|
| 420 |
+
NamedMorphism(Object("A"), Object("C"), "h")
|
| 421 |
+
|
| 422 |
+
"""
|
| 423 |
+
return NamedMorphism(self.domain, self.codomain, new_name)
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
class Category(Basic):
|
| 427 |
+
r"""
|
| 428 |
+
An (abstract) category.
|
| 429 |
+
|
| 430 |
+
Explanation
|
| 431 |
+
===========
|
| 432 |
+
|
| 433 |
+
A category [JoyOfCats] is a quadruple `\mbox{K} = (O, \hom, id,
|
| 434 |
+
\circ)` consisting of
|
| 435 |
+
|
| 436 |
+
* a (set-theoretical) class `O`, whose members are called
|
| 437 |
+
`K`-objects,
|
| 438 |
+
|
| 439 |
+
* for each pair `(A, B)` of `K`-objects, a set `\hom(A, B)` whose
|
| 440 |
+
members are called `K`-morphisms from `A` to `B`,
|
| 441 |
+
|
| 442 |
+
* for a each `K`-object `A`, a morphism `id:A\rightarrow A`,
|
| 443 |
+
called the `K`-identity of `A`,
|
| 444 |
+
|
| 445 |
+
* a composition law `\circ` associating with every `K`-morphisms
|
| 446 |
+
`f:A\rightarrow B` and `g:B\rightarrow C` a `K`-morphism `g\circ
|
| 447 |
+
f:A\rightarrow C`, called the composite of `f` and `g`.
|
| 448 |
+
|
| 449 |
+
Composition is associative, `K`-identities are identities with
|
| 450 |
+
respect to composition, and the sets `\hom(A, B)` are pairwise
|
| 451 |
+
disjoint.
|
| 452 |
+
|
| 453 |
+
This class knows nothing about its objects and morphisms.
|
| 454 |
+
Concrete cases of (abstract) categories should be implemented as
|
| 455 |
+
classes derived from this one.
|
| 456 |
+
|
| 457 |
+
Certain instances of :class:`Diagram` can be asserted to be
|
| 458 |
+
commutative in a :class:`Category` by supplying the argument
|
| 459 |
+
``commutative_diagrams`` in the constructor.
|
| 460 |
+
|
| 461 |
+
Examples
|
| 462 |
+
========
|
| 463 |
+
|
| 464 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram, Category
|
| 465 |
+
>>> from sympy import FiniteSet
|
| 466 |
+
>>> A = Object("A")
|
| 467 |
+
>>> B = Object("B")
|
| 468 |
+
>>> C = Object("C")
|
| 469 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 470 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 471 |
+
>>> d = Diagram([f, g])
|
| 472 |
+
>>> K = Category("K", commutative_diagrams=[d])
|
| 473 |
+
>>> K.commutative_diagrams == FiniteSet(d)
|
| 474 |
+
True
|
| 475 |
+
|
| 476 |
+
See Also
|
| 477 |
+
========
|
| 478 |
+
|
| 479 |
+
Diagram
|
| 480 |
+
"""
|
| 481 |
+
def __new__(cls, name, objects=EmptySet, commutative_diagrams=EmptySet):
|
| 482 |
+
if not name:
|
| 483 |
+
raise ValueError("A Category cannot have an empty name.")
|
| 484 |
+
|
| 485 |
+
if not isinstance(name, Str):
|
| 486 |
+
name = Str(name)
|
| 487 |
+
|
| 488 |
+
if not isinstance(objects, Class):
|
| 489 |
+
objects = Class(objects)
|
| 490 |
+
|
| 491 |
+
new_category = Basic.__new__(cls, name, objects,
|
| 492 |
+
FiniteSet(*commutative_diagrams))
|
| 493 |
+
return new_category
|
| 494 |
+
|
| 495 |
+
@property
|
| 496 |
+
def name(self):
|
| 497 |
+
"""
|
| 498 |
+
Returns the name of this category.
|
| 499 |
+
|
| 500 |
+
Examples
|
| 501 |
+
========
|
| 502 |
+
|
| 503 |
+
>>> from sympy.categories import Category
|
| 504 |
+
>>> K = Category("K")
|
| 505 |
+
>>> K.name
|
| 506 |
+
'K'
|
| 507 |
+
|
| 508 |
+
"""
|
| 509 |
+
return self.args[0].name
|
| 510 |
+
|
| 511 |
+
@property
|
| 512 |
+
def objects(self):
|
| 513 |
+
"""
|
| 514 |
+
Returns the class of objects of this category.
|
| 515 |
+
|
| 516 |
+
Examples
|
| 517 |
+
========
|
| 518 |
+
|
| 519 |
+
>>> from sympy.categories import Object, Category
|
| 520 |
+
>>> from sympy import FiniteSet
|
| 521 |
+
>>> A = Object("A")
|
| 522 |
+
>>> B = Object("B")
|
| 523 |
+
>>> K = Category("K", FiniteSet(A, B))
|
| 524 |
+
>>> K.objects
|
| 525 |
+
Class({Object("A"), Object("B")})
|
| 526 |
+
|
| 527 |
+
"""
|
| 528 |
+
return self.args[1]
|
| 529 |
+
|
| 530 |
+
@property
|
| 531 |
+
def commutative_diagrams(self):
|
| 532 |
+
"""
|
| 533 |
+
Returns the :class:`~.FiniteSet` of diagrams which are known to
|
| 534 |
+
be commutative in this category.
|
| 535 |
+
|
| 536 |
+
Examples
|
| 537 |
+
========
|
| 538 |
+
|
| 539 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram, Category
|
| 540 |
+
>>> from sympy import FiniteSet
|
| 541 |
+
>>> A = Object("A")
|
| 542 |
+
>>> B = Object("B")
|
| 543 |
+
>>> C = Object("C")
|
| 544 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 545 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 546 |
+
>>> d = Diagram([f, g])
|
| 547 |
+
>>> K = Category("K", commutative_diagrams=[d])
|
| 548 |
+
>>> K.commutative_diagrams == FiniteSet(d)
|
| 549 |
+
True
|
| 550 |
+
|
| 551 |
+
"""
|
| 552 |
+
return self.args[2]
|
| 553 |
+
|
| 554 |
+
def hom(self, A, B):
|
| 555 |
+
raise NotImplementedError(
|
| 556 |
+
"hom-sets are not implemented in Category.")
|
| 557 |
+
|
| 558 |
+
def all_morphisms(self):
|
| 559 |
+
raise NotImplementedError(
|
| 560 |
+
"Obtaining the class of morphisms is not implemented in Category.")
|
| 561 |
+
|
| 562 |
+
|
| 563 |
+
class Diagram(Basic):
|
| 564 |
+
r"""
|
| 565 |
+
Represents a diagram in a certain category.
|
| 566 |
+
|
| 567 |
+
Explanation
|
| 568 |
+
===========
|
| 569 |
+
|
| 570 |
+
Informally, a diagram is a collection of objects of a category and
|
| 571 |
+
certain morphisms between them. A diagram is still a monoid with
|
| 572 |
+
respect to morphism composition; i.e., identity morphisms, as well
|
| 573 |
+
as all composites of morphisms included in the diagram belong to
|
| 574 |
+
the diagram. For a more formal approach to this notion see
|
| 575 |
+
[Pare1970].
|
| 576 |
+
|
| 577 |
+
The components of composite morphisms are also added to the
|
| 578 |
+
diagram. No properties are assigned to such morphisms by default.
|
| 579 |
+
|
| 580 |
+
A commutative diagram is often accompanied by a statement of the
|
| 581 |
+
following kind: "if such morphisms with such properties exist,
|
| 582 |
+
then such morphisms which such properties exist and the diagram is
|
| 583 |
+
commutative". To represent this, an instance of :class:`Diagram`
|
| 584 |
+
includes a collection of morphisms which are the premises and
|
| 585 |
+
another collection of conclusions. ``premises`` and
|
| 586 |
+
``conclusions`` associate morphisms belonging to the corresponding
|
| 587 |
+
categories with the :class:`~.FiniteSet`'s of their properties.
|
| 588 |
+
|
| 589 |
+
The set of properties of a composite morphism is the intersection
|
| 590 |
+
of the sets of properties of its components. The domain and
|
| 591 |
+
codomain of a conclusion morphism should be among the domains and
|
| 592 |
+
codomains of the morphisms listed as the premises of a diagram.
|
| 593 |
+
|
| 594 |
+
No checks are carried out of whether the supplied object and
|
| 595 |
+
morphisms do belong to one and the same category.
|
| 596 |
+
|
| 597 |
+
Examples
|
| 598 |
+
========
|
| 599 |
+
|
| 600 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 601 |
+
>>> from sympy import pprint, default_sort_key
|
| 602 |
+
>>> A = Object("A")
|
| 603 |
+
>>> B = Object("B")
|
| 604 |
+
>>> C = Object("C")
|
| 605 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 606 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 607 |
+
>>> d = Diagram([f, g])
|
| 608 |
+
>>> premises_keys = sorted(d.premises.keys(), key=default_sort_key)
|
| 609 |
+
>>> pprint(premises_keys, use_unicode=False)
|
| 610 |
+
[g*f:A-->C, id:A-->A, id:B-->B, id:C-->C, f:A-->B, g:B-->C]
|
| 611 |
+
>>> pprint(d.premises, use_unicode=False)
|
| 612 |
+
{g*f:A-->C: EmptySet, id:A-->A: EmptySet, id:B-->B: EmptySet,
|
| 613 |
+
id:C-->C: EmptySet, f:A-->B: EmptySet, g:B-->C: EmptySet}
|
| 614 |
+
>>> d = Diagram([f, g], {g * f: "unique"})
|
| 615 |
+
>>> pprint(d.conclusions,use_unicode=False)
|
| 616 |
+
{g*f:A-->C: {unique}}
|
| 617 |
+
|
| 618 |
+
References
|
| 619 |
+
==========
|
| 620 |
+
|
| 621 |
+
[Pare1970] B. Pareigis: Categories and functors. Academic Press, 1970.
|
| 622 |
+
|
| 623 |
+
"""
|
| 624 |
+
@staticmethod
|
| 625 |
+
def _set_dict_union(dictionary, key, value):
|
| 626 |
+
"""
|
| 627 |
+
If ``key`` is in ``dictionary``, set the new value of ``key``
|
| 628 |
+
to be the union between the old value and ``value``.
|
| 629 |
+
Otherwise, set the value of ``key`` to ``value.
|
| 630 |
+
|
| 631 |
+
Returns ``True`` if the key already was in the dictionary and
|
| 632 |
+
``False`` otherwise.
|
| 633 |
+
"""
|
| 634 |
+
if key in dictionary:
|
| 635 |
+
dictionary[key] = dictionary[key] | value
|
| 636 |
+
return True
|
| 637 |
+
else:
|
| 638 |
+
dictionary[key] = value
|
| 639 |
+
return False
|
| 640 |
+
|
| 641 |
+
@staticmethod
|
| 642 |
+
def _add_morphism_closure(morphisms, morphism, props, add_identities=True,
|
| 643 |
+
recurse_composites=True):
|
| 644 |
+
"""
|
| 645 |
+
Adds a morphism and its attributes to the supplied dictionary
|
| 646 |
+
``morphisms``. If ``add_identities`` is True, also adds the
|
| 647 |
+
identity morphisms for the domain and the codomain of
|
| 648 |
+
``morphism``.
|
| 649 |
+
"""
|
| 650 |
+
if not Diagram._set_dict_union(morphisms, morphism, props):
|
| 651 |
+
# We have just added a new morphism.
|
| 652 |
+
|
| 653 |
+
if isinstance(morphism, IdentityMorphism):
|
| 654 |
+
if props:
|
| 655 |
+
# Properties for identity morphisms don't really
|
| 656 |
+
# make sense, because very much is known about
|
| 657 |
+
# identity morphisms already, so much that they
|
| 658 |
+
# are trivial. Having properties for identity
|
| 659 |
+
# morphisms would only be confusing.
|
| 660 |
+
raise ValueError(
|
| 661 |
+
"Instances of IdentityMorphism cannot have properties.")
|
| 662 |
+
return
|
| 663 |
+
|
| 664 |
+
if add_identities:
|
| 665 |
+
empty = EmptySet
|
| 666 |
+
|
| 667 |
+
id_dom = IdentityMorphism(morphism.domain)
|
| 668 |
+
id_cod = IdentityMorphism(morphism.codomain)
|
| 669 |
+
|
| 670 |
+
Diagram._set_dict_union(morphisms, id_dom, empty)
|
| 671 |
+
Diagram._set_dict_union(morphisms, id_cod, empty)
|
| 672 |
+
|
| 673 |
+
for existing_morphism, existing_props in list(morphisms.items()):
|
| 674 |
+
new_props = existing_props & props
|
| 675 |
+
if morphism.domain == existing_morphism.codomain:
|
| 676 |
+
left = morphism * existing_morphism
|
| 677 |
+
Diagram._set_dict_union(morphisms, left, new_props)
|
| 678 |
+
if morphism.codomain == existing_morphism.domain:
|
| 679 |
+
right = existing_morphism * morphism
|
| 680 |
+
Diagram._set_dict_union(morphisms, right, new_props)
|
| 681 |
+
|
| 682 |
+
if isinstance(morphism, CompositeMorphism) and recurse_composites:
|
| 683 |
+
# This is a composite morphism, add its components as
|
| 684 |
+
# well.
|
| 685 |
+
empty = EmptySet
|
| 686 |
+
for component in morphism.components:
|
| 687 |
+
Diagram._add_morphism_closure(morphisms, component, empty,
|
| 688 |
+
add_identities)
|
| 689 |
+
|
| 690 |
+
def __new__(cls, *args):
|
| 691 |
+
"""
|
| 692 |
+
Construct a new instance of Diagram.
|
| 693 |
+
|
| 694 |
+
Explanation
|
| 695 |
+
===========
|
| 696 |
+
|
| 697 |
+
If no arguments are supplied, an empty diagram is created.
|
| 698 |
+
|
| 699 |
+
If at least an argument is supplied, ``args[0]`` is
|
| 700 |
+
interpreted as the premises of the diagram. If ``args[0]`` is
|
| 701 |
+
a list, it is interpreted as a list of :class:`Morphism`'s, in
|
| 702 |
+
which each :class:`Morphism` has an empty set of properties.
|
| 703 |
+
If ``args[0]`` is a Python dictionary or a :class:`Dict`, it
|
| 704 |
+
is interpreted as a dictionary associating to some
|
| 705 |
+
:class:`Morphism`'s some properties.
|
| 706 |
+
|
| 707 |
+
If at least two arguments are supplied ``args[1]`` is
|
| 708 |
+
interpreted as the conclusions of the diagram. The type of
|
| 709 |
+
``args[1]`` is interpreted in exactly the same way as the type
|
| 710 |
+
of ``args[0]``. If only one argument is supplied, the diagram
|
| 711 |
+
has no conclusions.
|
| 712 |
+
|
| 713 |
+
Examples
|
| 714 |
+
========
|
| 715 |
+
|
| 716 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 717 |
+
>>> from sympy.categories import IdentityMorphism, Diagram
|
| 718 |
+
>>> A = Object("A")
|
| 719 |
+
>>> B = Object("B")
|
| 720 |
+
>>> C = Object("C")
|
| 721 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 722 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 723 |
+
>>> d = Diagram([f, g])
|
| 724 |
+
>>> IdentityMorphism(A) in d.premises.keys()
|
| 725 |
+
True
|
| 726 |
+
>>> g * f in d.premises.keys()
|
| 727 |
+
True
|
| 728 |
+
>>> d = Diagram([f, g], {g * f: "unique"})
|
| 729 |
+
>>> d.conclusions[g * f]
|
| 730 |
+
{unique}
|
| 731 |
+
|
| 732 |
+
"""
|
| 733 |
+
premises = {}
|
| 734 |
+
conclusions = {}
|
| 735 |
+
|
| 736 |
+
# Here we will keep track of the objects which appear in the
|
| 737 |
+
# premises.
|
| 738 |
+
objects = EmptySet
|
| 739 |
+
|
| 740 |
+
if len(args) >= 1:
|
| 741 |
+
# We've got some premises in the arguments.
|
| 742 |
+
premises_arg = args[0]
|
| 743 |
+
|
| 744 |
+
if isinstance(premises_arg, list):
|
| 745 |
+
# The user has supplied a list of morphisms, none of
|
| 746 |
+
# which have any attributes.
|
| 747 |
+
empty = EmptySet
|
| 748 |
+
|
| 749 |
+
for morphism in premises_arg:
|
| 750 |
+
objects |= FiniteSet(morphism.domain, morphism.codomain)
|
| 751 |
+
Diagram._add_morphism_closure(premises, morphism, empty)
|
| 752 |
+
elif isinstance(premises_arg, (dict, Dict)):
|
| 753 |
+
# The user has supplied a dictionary of morphisms and
|
| 754 |
+
# their properties.
|
| 755 |
+
for morphism, props in premises_arg.items():
|
| 756 |
+
objects |= FiniteSet(morphism.domain, morphism.codomain)
|
| 757 |
+
Diagram._add_morphism_closure(
|
| 758 |
+
premises, morphism, FiniteSet(*props) if iterable(props) else FiniteSet(props))
|
| 759 |
+
|
| 760 |
+
if len(args) >= 2:
|
| 761 |
+
# We also have some conclusions.
|
| 762 |
+
conclusions_arg = args[1]
|
| 763 |
+
|
| 764 |
+
if isinstance(conclusions_arg, list):
|
| 765 |
+
# The user has supplied a list of morphisms, none of
|
| 766 |
+
# which have any attributes.
|
| 767 |
+
empty = EmptySet
|
| 768 |
+
|
| 769 |
+
for morphism in conclusions_arg:
|
| 770 |
+
# Check that no new objects appear in conclusions.
|
| 771 |
+
if ((sympify(objects.contains(morphism.domain)) is S.true) and
|
| 772 |
+
(sympify(objects.contains(morphism.codomain)) is S.true)):
|
| 773 |
+
# No need to add identities and recurse
|
| 774 |
+
# composites this time.
|
| 775 |
+
Diagram._add_morphism_closure(
|
| 776 |
+
conclusions, morphism, empty, add_identities=False,
|
| 777 |
+
recurse_composites=False)
|
| 778 |
+
elif isinstance(conclusions_arg, (dict, Dict)):
|
| 779 |
+
# The user has supplied a dictionary of morphisms and
|
| 780 |
+
# their properties.
|
| 781 |
+
for morphism, props in conclusions_arg.items():
|
| 782 |
+
# Check that no new objects appear in conclusions.
|
| 783 |
+
if (morphism.domain in objects) and \
|
| 784 |
+
(morphism.codomain in objects):
|
| 785 |
+
# No need to add identities and recurse
|
| 786 |
+
# composites this time.
|
| 787 |
+
Diagram._add_morphism_closure(
|
| 788 |
+
conclusions, morphism, FiniteSet(*props) if iterable(props) else FiniteSet(props),
|
| 789 |
+
add_identities=False, recurse_composites=False)
|
| 790 |
+
|
| 791 |
+
return Basic.__new__(cls, Dict(premises), Dict(conclusions), objects)
|
| 792 |
+
|
| 793 |
+
@property
|
| 794 |
+
def premises(self):
|
| 795 |
+
"""
|
| 796 |
+
Returns the premises of this diagram.
|
| 797 |
+
|
| 798 |
+
Examples
|
| 799 |
+
========
|
| 800 |
+
|
| 801 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 802 |
+
>>> from sympy.categories import IdentityMorphism, Diagram
|
| 803 |
+
>>> from sympy import pretty
|
| 804 |
+
>>> A = Object("A")
|
| 805 |
+
>>> B = Object("B")
|
| 806 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 807 |
+
>>> id_A = IdentityMorphism(A)
|
| 808 |
+
>>> id_B = IdentityMorphism(B)
|
| 809 |
+
>>> d = Diagram([f])
|
| 810 |
+
>>> print(pretty(d.premises, use_unicode=False))
|
| 811 |
+
{id:A-->A: EmptySet, id:B-->B: EmptySet, f:A-->B: EmptySet}
|
| 812 |
+
|
| 813 |
+
"""
|
| 814 |
+
return self.args[0]
|
| 815 |
+
|
| 816 |
+
@property
|
| 817 |
+
def conclusions(self):
|
| 818 |
+
"""
|
| 819 |
+
Returns the conclusions of this diagram.
|
| 820 |
+
|
| 821 |
+
Examples
|
| 822 |
+
========
|
| 823 |
+
|
| 824 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 825 |
+
>>> from sympy.categories import IdentityMorphism, Diagram
|
| 826 |
+
>>> from sympy import FiniteSet
|
| 827 |
+
>>> A = Object("A")
|
| 828 |
+
>>> B = Object("B")
|
| 829 |
+
>>> C = Object("C")
|
| 830 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 831 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 832 |
+
>>> d = Diagram([f, g])
|
| 833 |
+
>>> IdentityMorphism(A) in d.premises.keys()
|
| 834 |
+
True
|
| 835 |
+
>>> g * f in d.premises.keys()
|
| 836 |
+
True
|
| 837 |
+
>>> d = Diagram([f, g], {g * f: "unique"})
|
| 838 |
+
>>> d.conclusions[g * f] == FiniteSet("unique")
|
| 839 |
+
True
|
| 840 |
+
|
| 841 |
+
"""
|
| 842 |
+
return self.args[1]
|
| 843 |
+
|
| 844 |
+
@property
|
| 845 |
+
def objects(self):
|
| 846 |
+
"""
|
| 847 |
+
Returns the :class:`~.FiniteSet` of objects that appear in this
|
| 848 |
+
diagram.
|
| 849 |
+
|
| 850 |
+
Examples
|
| 851 |
+
========
|
| 852 |
+
|
| 853 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 854 |
+
>>> A = Object("A")
|
| 855 |
+
>>> B = Object("B")
|
| 856 |
+
>>> C = Object("C")
|
| 857 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 858 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 859 |
+
>>> d = Diagram([f, g])
|
| 860 |
+
>>> d.objects
|
| 861 |
+
{Object("A"), Object("B"), Object("C")}
|
| 862 |
+
|
| 863 |
+
"""
|
| 864 |
+
return self.args[2]
|
| 865 |
+
|
| 866 |
+
def hom(self, A, B):
|
| 867 |
+
"""
|
| 868 |
+
Returns a 2-tuple of sets of morphisms between objects ``A`` and
|
| 869 |
+
``B``: one set of morphisms listed as premises, and the other set
|
| 870 |
+
of morphisms listed as conclusions.
|
| 871 |
+
|
| 872 |
+
Examples
|
| 873 |
+
========
|
| 874 |
+
|
| 875 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 876 |
+
>>> from sympy import pretty
|
| 877 |
+
>>> A = Object("A")
|
| 878 |
+
>>> B = Object("B")
|
| 879 |
+
>>> C = Object("C")
|
| 880 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 881 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 882 |
+
>>> d = Diagram([f, g], {g * f: "unique"})
|
| 883 |
+
>>> print(pretty(d.hom(A, C), use_unicode=False))
|
| 884 |
+
({g*f:A-->C}, {g*f:A-->C})
|
| 885 |
+
|
| 886 |
+
See Also
|
| 887 |
+
========
|
| 888 |
+
Object, Morphism
|
| 889 |
+
"""
|
| 890 |
+
premises = EmptySet
|
| 891 |
+
conclusions = EmptySet
|
| 892 |
+
|
| 893 |
+
for morphism in self.premises.keys():
|
| 894 |
+
if (morphism.domain == A) and (morphism.codomain == B):
|
| 895 |
+
premises |= FiniteSet(morphism)
|
| 896 |
+
for morphism in self.conclusions.keys():
|
| 897 |
+
if (morphism.domain == A) and (morphism.codomain == B):
|
| 898 |
+
conclusions |= FiniteSet(morphism)
|
| 899 |
+
|
| 900 |
+
return (premises, conclusions)
|
| 901 |
+
|
| 902 |
+
def is_subdiagram(self, diagram):
|
| 903 |
+
"""
|
| 904 |
+
Checks whether ``diagram`` is a subdiagram of ``self``.
|
| 905 |
+
Diagram `D'` is a subdiagram of `D` if all premises
|
| 906 |
+
(conclusions) of `D'` are contained in the premises
|
| 907 |
+
(conclusions) of `D`. The morphisms contained
|
| 908 |
+
both in `D'` and `D` should have the same properties for `D'`
|
| 909 |
+
to be a subdiagram of `D`.
|
| 910 |
+
|
| 911 |
+
Examples
|
| 912 |
+
========
|
| 913 |
+
|
| 914 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 915 |
+
>>> A = Object("A")
|
| 916 |
+
>>> B = Object("B")
|
| 917 |
+
>>> C = Object("C")
|
| 918 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 919 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 920 |
+
>>> d = Diagram([f, g], {g * f: "unique"})
|
| 921 |
+
>>> d1 = Diagram([f])
|
| 922 |
+
>>> d.is_subdiagram(d1)
|
| 923 |
+
True
|
| 924 |
+
>>> d1.is_subdiagram(d)
|
| 925 |
+
False
|
| 926 |
+
"""
|
| 927 |
+
premises = all((m in self.premises) and
|
| 928 |
+
(diagram.premises[m] == self.premises[m])
|
| 929 |
+
for m in diagram.premises)
|
| 930 |
+
if not premises:
|
| 931 |
+
return False
|
| 932 |
+
|
| 933 |
+
conclusions = all((m in self.conclusions) and
|
| 934 |
+
(diagram.conclusions[m] == self.conclusions[m])
|
| 935 |
+
for m in diagram.conclusions)
|
| 936 |
+
|
| 937 |
+
# Premises is surely ``True`` here.
|
| 938 |
+
return conclusions
|
| 939 |
+
|
| 940 |
+
def subdiagram_from_objects(self, objects):
|
| 941 |
+
"""
|
| 942 |
+
If ``objects`` is a subset of the objects of ``self``, returns
|
| 943 |
+
a diagram which has as premises all those premises of ``self``
|
| 944 |
+
which have a domains and codomains in ``objects``, likewise
|
| 945 |
+
for conclusions. Properties are preserved.
|
| 946 |
+
|
| 947 |
+
Examples
|
| 948 |
+
========
|
| 949 |
+
|
| 950 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 951 |
+
>>> from sympy import FiniteSet
|
| 952 |
+
>>> A = Object("A")
|
| 953 |
+
>>> B = Object("B")
|
| 954 |
+
>>> C = Object("C")
|
| 955 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 956 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 957 |
+
>>> d = Diagram([f, g], {f: "unique", g*f: "veryunique"})
|
| 958 |
+
>>> d1 = d.subdiagram_from_objects(FiniteSet(A, B))
|
| 959 |
+
>>> d1 == Diagram([f], {f: "unique"})
|
| 960 |
+
True
|
| 961 |
+
"""
|
| 962 |
+
if not objects.is_subset(self.objects):
|
| 963 |
+
raise ValueError(
|
| 964 |
+
"Supplied objects should all belong to the diagram.")
|
| 965 |
+
|
| 966 |
+
new_premises = {}
|
| 967 |
+
for morphism, props in self.premises.items():
|
| 968 |
+
if ((sympify(objects.contains(morphism.domain)) is S.true) and
|
| 969 |
+
(sympify(objects.contains(morphism.codomain)) is S.true)):
|
| 970 |
+
new_premises[morphism] = props
|
| 971 |
+
|
| 972 |
+
new_conclusions = {}
|
| 973 |
+
for morphism, props in self.conclusions.items():
|
| 974 |
+
if ((sympify(objects.contains(morphism.domain)) is S.true) and
|
| 975 |
+
(sympify(objects.contains(morphism.codomain)) is S.true)):
|
| 976 |
+
new_conclusions[morphism] = props
|
| 977 |
+
|
| 978 |
+
return Diagram(new_premises, new_conclusions)
|
lib/python3.12/site-packages/sympy/categories/diagram_drawing.py
ADDED
|
@@ -0,0 +1,2582 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""
|
| 2 |
+
This module contains the functionality to arrange the nodes of a
|
| 3 |
+
diagram on an abstract grid, and then to produce a graphical
|
| 4 |
+
representation of the grid.
|
| 5 |
+
|
| 6 |
+
The currently supported back-ends are Xy-pic [Xypic].
|
| 7 |
+
|
| 8 |
+
Layout Algorithm
|
| 9 |
+
================
|
| 10 |
+
|
| 11 |
+
This section provides an overview of the algorithms implemented in
|
| 12 |
+
:class:`DiagramGrid` to lay out diagrams.
|
| 13 |
+
|
| 14 |
+
The first step of the algorithm is the removal composite and identity
|
| 15 |
+
morphisms which do not have properties in the supplied diagram. The
|
| 16 |
+
premises and conclusions of the diagram are then merged.
|
| 17 |
+
|
| 18 |
+
The generic layout algorithm begins with the construction of the
|
| 19 |
+
"skeleton" of the diagram. The skeleton is an undirected graph which
|
| 20 |
+
has the objects of the diagram as vertices and has an (undirected)
|
| 21 |
+
edge between each pair of objects between which there exist morphisms.
|
| 22 |
+
The direction of the morphisms does not matter at this stage. The
|
| 23 |
+
skeleton also includes an edge between each pair of vertices `A` and
|
| 24 |
+
`C` such that there exists an object `B` which is connected via
|
| 25 |
+
a morphism to `A`, and via a morphism to `C`.
|
| 26 |
+
|
| 27 |
+
The skeleton constructed in this way has the property that every
|
| 28 |
+
object is a vertex of a triangle formed by three edges of the
|
| 29 |
+
skeleton. This property lies at the base of the generic layout
|
| 30 |
+
algorithm.
|
| 31 |
+
|
| 32 |
+
After the skeleton has been constructed, the algorithm lists all
|
| 33 |
+
triangles which can be formed. Note that some triangles will not have
|
| 34 |
+
all edges corresponding to morphisms which will actually be drawn.
|
| 35 |
+
Triangles which have only one edge or less which will actually be
|
| 36 |
+
drawn are immediately discarded.
|
| 37 |
+
|
| 38 |
+
The list of triangles is sorted according to the number of edges which
|
| 39 |
+
correspond to morphisms, then the triangle with the least number of such
|
| 40 |
+
edges is selected. One of such edges is picked and the corresponding
|
| 41 |
+
objects are placed horizontally, on a grid. This edge is recorded to
|
| 42 |
+
be in the fringe. The algorithm then finds a "welding" of a triangle
|
| 43 |
+
to the fringe. A welding is an edge in the fringe where a triangle
|
| 44 |
+
could be attached. If the algorithm succeeds in finding such a
|
| 45 |
+
welding, it adds to the grid that vertex of the triangle which was not
|
| 46 |
+
yet included in any edge in the fringe and records the two new edges in
|
| 47 |
+
the fringe. This process continues iteratively until all objects of
|
| 48 |
+
the diagram has been placed or until no more weldings can be found.
|
| 49 |
+
|
| 50 |
+
An edge is only removed from the fringe when a welding to this edge
|
| 51 |
+
has been found, and there is no room around this edge to place
|
| 52 |
+
another vertex.
|
| 53 |
+
|
| 54 |
+
When no more weldings can be found, but there are still triangles
|
| 55 |
+
left, the algorithm searches for a possibility of attaching one of the
|
| 56 |
+
remaining triangles to the existing structure by a vertex. If such a
|
| 57 |
+
possibility is found, the corresponding edge of the found triangle is
|
| 58 |
+
placed in the found space and the iterative process of welding
|
| 59 |
+
triangles restarts.
|
| 60 |
+
|
| 61 |
+
When logical groups are supplied, each of these groups is laid out
|
| 62 |
+
independently. Then a diagram is constructed in which groups are
|
| 63 |
+
objects and any two logical groups between which there exist morphisms
|
| 64 |
+
are connected via a morphism. This diagram is laid out. Finally,
|
| 65 |
+
the grid which includes all objects of the initial diagram is
|
| 66 |
+
constructed by replacing the cells which contain logical groups with
|
| 67 |
+
the corresponding laid out grids, and by correspondingly expanding the
|
| 68 |
+
rows and columns.
|
| 69 |
+
|
| 70 |
+
The sequential layout algorithm begins by constructing the
|
| 71 |
+
underlying undirected graph defined by the morphisms obtained after
|
| 72 |
+
simplifying premises and conclusions and merging them (see above).
|
| 73 |
+
The vertex with the minimal degree is then picked up and depth-first
|
| 74 |
+
search is started from it. All objects which are located at distance
|
| 75 |
+
`n` from the root in the depth-first search tree, are positioned in
|
| 76 |
+
the `n`-th column of the resulting grid. The sequential layout will
|
| 77 |
+
therefore attempt to lay the objects out along a line.
|
| 78 |
+
|
| 79 |
+
References
|
| 80 |
+
==========
|
| 81 |
+
|
| 82 |
+
.. [Xypic] https://xy-pic.sourceforge.net/
|
| 83 |
+
|
| 84 |
+
"""
|
| 85 |
+
from sympy.categories import (CompositeMorphism, IdentityMorphism,
|
| 86 |
+
NamedMorphism, Diagram)
|
| 87 |
+
from sympy.core import Dict, Symbol, default_sort_key
|
| 88 |
+
from sympy.printing.latex import latex
|
| 89 |
+
from sympy.sets import FiniteSet
|
| 90 |
+
from sympy.utilities.iterables import iterable
|
| 91 |
+
from sympy.utilities.decorator import doctest_depends_on
|
| 92 |
+
|
| 93 |
+
from itertools import chain
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
__doctest_requires__ = {('preview_diagram',): 'pyglet'}
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class _GrowableGrid:
|
| 100 |
+
"""
|
| 101 |
+
Holds a growable grid of objects.
|
| 102 |
+
|
| 103 |
+
Explanation
|
| 104 |
+
===========
|
| 105 |
+
|
| 106 |
+
It is possible to append or prepend a row or a column to the grid
|
| 107 |
+
using the corresponding methods. Prepending rows or columns has
|
| 108 |
+
the effect of changing the coordinates of the already existing
|
| 109 |
+
elements.
|
| 110 |
+
|
| 111 |
+
This class currently represents a naive implementation of the
|
| 112 |
+
functionality with little attempt at optimisation.
|
| 113 |
+
"""
|
| 114 |
+
def __init__(self, width, height):
|
| 115 |
+
self._width = width
|
| 116 |
+
self._height = height
|
| 117 |
+
|
| 118 |
+
self._array = [[None for j in range(width)] for i in range(height)]
|
| 119 |
+
|
| 120 |
+
@property
|
| 121 |
+
def width(self):
|
| 122 |
+
return self._width
|
| 123 |
+
|
| 124 |
+
@property
|
| 125 |
+
def height(self):
|
| 126 |
+
return self._height
|
| 127 |
+
|
| 128 |
+
def __getitem__(self, i_j):
|
| 129 |
+
"""
|
| 130 |
+
Returns the element located at in the i-th line and j-th
|
| 131 |
+
column.
|
| 132 |
+
"""
|
| 133 |
+
i, j = i_j
|
| 134 |
+
return self._array[i][j]
|
| 135 |
+
|
| 136 |
+
def __setitem__(self, i_j, newvalue):
|
| 137 |
+
"""
|
| 138 |
+
Sets the element located at in the i-th line and j-th
|
| 139 |
+
column.
|
| 140 |
+
"""
|
| 141 |
+
i, j = i_j
|
| 142 |
+
self._array[i][j] = newvalue
|
| 143 |
+
|
| 144 |
+
def append_row(self):
|
| 145 |
+
"""
|
| 146 |
+
Appends an empty row to the grid.
|
| 147 |
+
"""
|
| 148 |
+
self._height += 1
|
| 149 |
+
self._array.append([None for j in range(self._width)])
|
| 150 |
+
|
| 151 |
+
def append_column(self):
|
| 152 |
+
"""
|
| 153 |
+
Appends an empty column to the grid.
|
| 154 |
+
"""
|
| 155 |
+
self._width += 1
|
| 156 |
+
for i in range(self._height):
|
| 157 |
+
self._array[i].append(None)
|
| 158 |
+
|
| 159 |
+
def prepend_row(self):
|
| 160 |
+
"""
|
| 161 |
+
Prepends the grid with an empty row.
|
| 162 |
+
"""
|
| 163 |
+
self._height += 1
|
| 164 |
+
self._array.insert(0, [None for j in range(self._width)])
|
| 165 |
+
|
| 166 |
+
def prepend_column(self):
|
| 167 |
+
"""
|
| 168 |
+
Prepends the grid with an empty column.
|
| 169 |
+
"""
|
| 170 |
+
self._width += 1
|
| 171 |
+
for i in range(self._height):
|
| 172 |
+
self._array[i].insert(0, None)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class DiagramGrid:
|
| 176 |
+
r"""
|
| 177 |
+
Constructs and holds the fitting of the diagram into a grid.
|
| 178 |
+
|
| 179 |
+
Explanation
|
| 180 |
+
===========
|
| 181 |
+
|
| 182 |
+
The mission of this class is to analyse the structure of the
|
| 183 |
+
supplied diagram and to place its objects on a grid such that,
|
| 184 |
+
when the objects and the morphisms are actually drawn, the diagram
|
| 185 |
+
would be "readable", in the sense that there will not be many
|
| 186 |
+
intersections of moprhisms. This class does not perform any
|
| 187 |
+
actual drawing. It does strive nevertheless to offer sufficient
|
| 188 |
+
metadata to draw a diagram.
|
| 189 |
+
|
| 190 |
+
Consider the following simple diagram.
|
| 191 |
+
|
| 192 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 193 |
+
>>> from sympy.categories import Diagram, DiagramGrid
|
| 194 |
+
>>> from sympy import pprint
|
| 195 |
+
>>> A = Object("A")
|
| 196 |
+
>>> B = Object("B")
|
| 197 |
+
>>> C = Object("C")
|
| 198 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 199 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 200 |
+
>>> diagram = Diagram([f, g])
|
| 201 |
+
|
| 202 |
+
The simplest way to have a diagram laid out is the following:
|
| 203 |
+
|
| 204 |
+
>>> grid = DiagramGrid(diagram)
|
| 205 |
+
>>> (grid.width, grid.height)
|
| 206 |
+
(2, 2)
|
| 207 |
+
>>> pprint(grid)
|
| 208 |
+
A B
|
| 209 |
+
<BLANKLINE>
|
| 210 |
+
C
|
| 211 |
+
|
| 212 |
+
Sometimes one sees the diagram as consisting of logical groups.
|
| 213 |
+
One can advise ``DiagramGrid`` as to such groups by employing the
|
| 214 |
+
``groups`` keyword argument.
|
| 215 |
+
|
| 216 |
+
Consider the following diagram:
|
| 217 |
+
|
| 218 |
+
>>> D = Object("D")
|
| 219 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 220 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 221 |
+
>>> h = NamedMorphism(D, A, "h")
|
| 222 |
+
>>> k = NamedMorphism(D, B, "k")
|
| 223 |
+
>>> diagram = Diagram([f, g, h, k])
|
| 224 |
+
|
| 225 |
+
Lay it out with generic layout:
|
| 226 |
+
|
| 227 |
+
>>> grid = DiagramGrid(diagram)
|
| 228 |
+
>>> pprint(grid)
|
| 229 |
+
A B D
|
| 230 |
+
<BLANKLINE>
|
| 231 |
+
C
|
| 232 |
+
|
| 233 |
+
Now, we can group the objects `A` and `D` to have them near one
|
| 234 |
+
another:
|
| 235 |
+
|
| 236 |
+
>>> grid = DiagramGrid(diagram, groups=[[A, D], B, C])
|
| 237 |
+
>>> pprint(grid)
|
| 238 |
+
B C
|
| 239 |
+
<BLANKLINE>
|
| 240 |
+
A D
|
| 241 |
+
|
| 242 |
+
Note how the positioning of the other objects changes.
|
| 243 |
+
|
| 244 |
+
Further indications can be supplied to the constructor of
|
| 245 |
+
:class:`DiagramGrid` using keyword arguments. The currently
|
| 246 |
+
supported hints are explained in the following paragraphs.
|
| 247 |
+
|
| 248 |
+
:class:`DiagramGrid` does not automatically guess which layout
|
| 249 |
+
would suit the supplied diagram better. Consider, for example,
|
| 250 |
+
the following linear diagram:
|
| 251 |
+
|
| 252 |
+
>>> E = Object("E")
|
| 253 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 254 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 255 |
+
>>> h = NamedMorphism(C, D, "h")
|
| 256 |
+
>>> i = NamedMorphism(D, E, "i")
|
| 257 |
+
>>> diagram = Diagram([f, g, h, i])
|
| 258 |
+
|
| 259 |
+
When laid out with the generic layout, it does not get to look
|
| 260 |
+
linear:
|
| 261 |
+
|
| 262 |
+
>>> grid = DiagramGrid(diagram)
|
| 263 |
+
>>> pprint(grid)
|
| 264 |
+
A B
|
| 265 |
+
<BLANKLINE>
|
| 266 |
+
C D
|
| 267 |
+
<BLANKLINE>
|
| 268 |
+
E
|
| 269 |
+
|
| 270 |
+
To get it laid out in a line, use ``layout="sequential"``:
|
| 271 |
+
|
| 272 |
+
>>> grid = DiagramGrid(diagram, layout="sequential")
|
| 273 |
+
>>> pprint(grid)
|
| 274 |
+
A B C D E
|
| 275 |
+
|
| 276 |
+
One may sometimes need to transpose the resulting layout. While
|
| 277 |
+
this can always be done by hand, :class:`DiagramGrid` provides a
|
| 278 |
+
hint for that purpose:
|
| 279 |
+
|
| 280 |
+
>>> grid = DiagramGrid(diagram, layout="sequential", transpose=True)
|
| 281 |
+
>>> pprint(grid)
|
| 282 |
+
A
|
| 283 |
+
<BLANKLINE>
|
| 284 |
+
B
|
| 285 |
+
<BLANKLINE>
|
| 286 |
+
C
|
| 287 |
+
<BLANKLINE>
|
| 288 |
+
D
|
| 289 |
+
<BLANKLINE>
|
| 290 |
+
E
|
| 291 |
+
|
| 292 |
+
Separate hints can also be provided for each group. For an
|
| 293 |
+
example, refer to ``tests/test_drawing.py``, and see the different
|
| 294 |
+
ways in which the five lemma [FiveLemma] can be laid out.
|
| 295 |
+
|
| 296 |
+
See Also
|
| 297 |
+
========
|
| 298 |
+
|
| 299 |
+
Diagram
|
| 300 |
+
|
| 301 |
+
References
|
| 302 |
+
==========
|
| 303 |
+
|
| 304 |
+
.. [FiveLemma] https://en.wikipedia.org/wiki/Five_lemma
|
| 305 |
+
"""
|
| 306 |
+
@staticmethod
|
| 307 |
+
def _simplify_morphisms(morphisms):
|
| 308 |
+
"""
|
| 309 |
+
Given a dictionary mapping morphisms to their properties,
|
| 310 |
+
returns a new dictionary in which there are no morphisms which
|
| 311 |
+
do not have properties, and which are compositions of other
|
| 312 |
+
morphisms included in the dictionary. Identities are dropped
|
| 313 |
+
as well.
|
| 314 |
+
"""
|
| 315 |
+
newmorphisms = {}
|
| 316 |
+
for morphism, props in morphisms.items():
|
| 317 |
+
if isinstance(morphism, CompositeMorphism) and not props:
|
| 318 |
+
continue
|
| 319 |
+
elif isinstance(morphism, IdentityMorphism):
|
| 320 |
+
continue
|
| 321 |
+
else:
|
| 322 |
+
newmorphisms[morphism] = props
|
| 323 |
+
return newmorphisms
|
| 324 |
+
|
| 325 |
+
@staticmethod
|
| 326 |
+
def _merge_premises_conclusions(premises, conclusions):
|
| 327 |
+
"""
|
| 328 |
+
Given two dictionaries of morphisms and their properties,
|
| 329 |
+
produces a single dictionary which includes elements from both
|
| 330 |
+
dictionaries. If a morphism has some properties in premises
|
| 331 |
+
and also in conclusions, the properties in conclusions take
|
| 332 |
+
priority.
|
| 333 |
+
"""
|
| 334 |
+
return dict(chain(premises.items(), conclusions.items()))
|
| 335 |
+
|
| 336 |
+
@staticmethod
|
| 337 |
+
def _juxtapose_edges(edge1, edge2):
|
| 338 |
+
"""
|
| 339 |
+
If ``edge1`` and ``edge2`` have precisely one common endpoint,
|
| 340 |
+
returns an edge which would form a triangle with ``edge1`` and
|
| 341 |
+
``edge2``.
|
| 342 |
+
|
| 343 |
+
If ``edge1`` and ``edge2`` do not have a common endpoint,
|
| 344 |
+
returns ``None``.
|
| 345 |
+
|
| 346 |
+
If ``edge1`` and ``edge`` are the same edge, returns ``None``.
|
| 347 |
+
"""
|
| 348 |
+
intersection = edge1 & edge2
|
| 349 |
+
if len(intersection) != 1:
|
| 350 |
+
# The edges either have no common points or are equal.
|
| 351 |
+
return None
|
| 352 |
+
|
| 353 |
+
# The edges have a common endpoint. Extract the different
|
| 354 |
+
# endpoints and set up the new edge.
|
| 355 |
+
return (edge1 - intersection) | (edge2 - intersection)
|
| 356 |
+
|
| 357 |
+
@staticmethod
|
| 358 |
+
def _add_edge_append(dictionary, edge, elem):
|
| 359 |
+
"""
|
| 360 |
+
If ``edge`` is not in ``dictionary``, adds ``edge`` to the
|
| 361 |
+
dictionary and sets its value to ``[elem]``. Otherwise
|
| 362 |
+
appends ``elem`` to the value of existing entry.
|
| 363 |
+
|
| 364 |
+
Note that edges are undirected, thus `(A, B) = (B, A)`.
|
| 365 |
+
"""
|
| 366 |
+
if edge in dictionary:
|
| 367 |
+
dictionary[edge].append(elem)
|
| 368 |
+
else:
|
| 369 |
+
dictionary[edge] = [elem]
|
| 370 |
+
|
| 371 |
+
@staticmethod
|
| 372 |
+
def _build_skeleton(morphisms):
|
| 373 |
+
"""
|
| 374 |
+
Creates a dictionary which maps edges to corresponding
|
| 375 |
+
morphisms. Thus for a morphism `f:A\rightarrow B`, the edge
|
| 376 |
+
`(A, B)` will be associated with `f`. This function also adds
|
| 377 |
+
to the list those edges which are formed by juxtaposition of
|
| 378 |
+
two edges already in the list. These new edges are not
|
| 379 |
+
associated with any morphism and are only added to assure that
|
| 380 |
+
the diagram can be decomposed into triangles.
|
| 381 |
+
"""
|
| 382 |
+
edges = {}
|
| 383 |
+
# Create edges for morphisms.
|
| 384 |
+
for morphism in morphisms:
|
| 385 |
+
DiagramGrid._add_edge_append(
|
| 386 |
+
edges, frozenset([morphism.domain, morphism.codomain]), morphism)
|
| 387 |
+
|
| 388 |
+
# Create new edges by juxtaposing existing edges.
|
| 389 |
+
edges1 = dict(edges)
|
| 390 |
+
for w in edges1:
|
| 391 |
+
for v in edges1:
|
| 392 |
+
wv = DiagramGrid._juxtapose_edges(w, v)
|
| 393 |
+
if wv and wv not in edges:
|
| 394 |
+
edges[wv] = []
|
| 395 |
+
|
| 396 |
+
return edges
|
| 397 |
+
|
| 398 |
+
@staticmethod
|
| 399 |
+
def _list_triangles(edges):
|
| 400 |
+
"""
|
| 401 |
+
Builds the set of triangles formed by the supplied edges. The
|
| 402 |
+
triangles are arbitrary and need not be commutative. A
|
| 403 |
+
triangle is a set that contains all three of its sides.
|
| 404 |
+
"""
|
| 405 |
+
triangles = set()
|
| 406 |
+
|
| 407 |
+
for w in edges:
|
| 408 |
+
for v in edges:
|
| 409 |
+
wv = DiagramGrid._juxtapose_edges(w, v)
|
| 410 |
+
if wv and wv in edges:
|
| 411 |
+
triangles.add(frozenset([w, v, wv]))
|
| 412 |
+
|
| 413 |
+
return triangles
|
| 414 |
+
|
| 415 |
+
@staticmethod
|
| 416 |
+
def _drop_redundant_triangles(triangles, skeleton):
|
| 417 |
+
"""
|
| 418 |
+
Returns a list which contains only those triangles who have
|
| 419 |
+
morphisms associated with at least two edges.
|
| 420 |
+
"""
|
| 421 |
+
return [tri for tri in triangles
|
| 422 |
+
if len([e for e in tri if skeleton[e]]) >= 2]
|
| 423 |
+
|
| 424 |
+
@staticmethod
|
| 425 |
+
def _morphism_length(morphism):
|
| 426 |
+
"""
|
| 427 |
+
Returns the length of a morphism. The length of a morphism is
|
| 428 |
+
the number of components it consists of. A non-composite
|
| 429 |
+
morphism is of length 1.
|
| 430 |
+
"""
|
| 431 |
+
if isinstance(morphism, CompositeMorphism):
|
| 432 |
+
return len(morphism.components)
|
| 433 |
+
else:
|
| 434 |
+
return 1
|
| 435 |
+
|
| 436 |
+
@staticmethod
|
| 437 |
+
def _compute_triangle_min_sizes(triangles, edges):
|
| 438 |
+
r"""
|
| 439 |
+
Returns a dictionary mapping triangles to their minimal sizes.
|
| 440 |
+
The minimal size of a triangle is the sum of maximal lengths
|
| 441 |
+
of morphisms associated to the sides of the triangle. The
|
| 442 |
+
length of a morphism is the number of components it consists
|
| 443 |
+
of. A non-composite morphism is of length 1.
|
| 444 |
+
|
| 445 |
+
Sorting triangles by this metric attempts to address two
|
| 446 |
+
aspects of layout. For triangles with only simple morphisms
|
| 447 |
+
in the edge, this assures that triangles with all three edges
|
| 448 |
+
visible will get typeset after triangles with less visible
|
| 449 |
+
edges, which sometimes minimizes the necessity in diagonal
|
| 450 |
+
arrows. For triangles with composite morphisms in the edges,
|
| 451 |
+
this assures that objects connected with shorter morphisms
|
| 452 |
+
will be laid out first, resulting the visual proximity of
|
| 453 |
+
those objects which are connected by shorter morphisms.
|
| 454 |
+
"""
|
| 455 |
+
triangle_sizes = {}
|
| 456 |
+
for triangle in triangles:
|
| 457 |
+
size = 0
|
| 458 |
+
for e in triangle:
|
| 459 |
+
morphisms = edges[e]
|
| 460 |
+
if morphisms:
|
| 461 |
+
size += max(DiagramGrid._morphism_length(m)
|
| 462 |
+
for m in morphisms)
|
| 463 |
+
triangle_sizes[triangle] = size
|
| 464 |
+
return triangle_sizes
|
| 465 |
+
|
| 466 |
+
@staticmethod
|
| 467 |
+
def _triangle_objects(triangle):
|
| 468 |
+
"""
|
| 469 |
+
Given a triangle, returns the objects included in it.
|
| 470 |
+
"""
|
| 471 |
+
# A triangle is a frozenset of three two-element frozensets
|
| 472 |
+
# (the edges). This chains the three edges together and
|
| 473 |
+
# creates a frozenset from the iterator, thus producing a
|
| 474 |
+
# frozenset of objects of the triangle.
|
| 475 |
+
return frozenset(chain(*tuple(triangle)))
|
| 476 |
+
|
| 477 |
+
@staticmethod
|
| 478 |
+
def _other_vertex(triangle, edge):
|
| 479 |
+
"""
|
| 480 |
+
Given a triangle and an edge of it, returns the vertex which
|
| 481 |
+
opposes the edge.
|
| 482 |
+
"""
|
| 483 |
+
# This gets the set of objects of the triangle and then
|
| 484 |
+
# subtracts the set of objects employed in ``edge`` to get the
|
| 485 |
+
# vertex opposite to ``edge``.
|
| 486 |
+
return list(DiagramGrid._triangle_objects(triangle) - set(edge))[0]
|
| 487 |
+
|
| 488 |
+
@staticmethod
|
| 489 |
+
def _empty_point(pt, grid):
|
| 490 |
+
"""
|
| 491 |
+
Checks if the cell at coordinates ``pt`` is either empty or
|
| 492 |
+
out of the bounds of the grid.
|
| 493 |
+
"""
|
| 494 |
+
if (pt[0] < 0) or (pt[1] < 0) or \
|
| 495 |
+
(pt[0] >= grid.height) or (pt[1] >= grid.width):
|
| 496 |
+
return True
|
| 497 |
+
return grid[pt] is None
|
| 498 |
+
|
| 499 |
+
@staticmethod
|
| 500 |
+
def _put_object(coords, obj, grid, fringe):
|
| 501 |
+
"""
|
| 502 |
+
Places an object at the coordinate ``cords`` in ``grid``,
|
| 503 |
+
growing the grid and updating ``fringe``, if necessary.
|
| 504 |
+
Returns (0, 0) if no row or column has been prepended, (1, 0)
|
| 505 |
+
if a row was prepended, (0, 1) if a column was prepended and
|
| 506 |
+
(1, 1) if both a column and a row were prepended.
|
| 507 |
+
"""
|
| 508 |
+
(i, j) = coords
|
| 509 |
+
offset = (0, 0)
|
| 510 |
+
if i == -1:
|
| 511 |
+
grid.prepend_row()
|
| 512 |
+
i = 0
|
| 513 |
+
offset = (1, 0)
|
| 514 |
+
for k in range(len(fringe)):
|
| 515 |
+
((i1, j1), (i2, j2)) = fringe[k]
|
| 516 |
+
fringe[k] = ((i1 + 1, j1), (i2 + 1, j2))
|
| 517 |
+
elif i == grid.height:
|
| 518 |
+
grid.append_row()
|
| 519 |
+
|
| 520 |
+
if j == -1:
|
| 521 |
+
j = 0
|
| 522 |
+
offset = (offset[0], 1)
|
| 523 |
+
grid.prepend_column()
|
| 524 |
+
for k in range(len(fringe)):
|
| 525 |
+
((i1, j1), (i2, j2)) = fringe[k]
|
| 526 |
+
fringe[k] = ((i1, j1 + 1), (i2, j2 + 1))
|
| 527 |
+
elif j == grid.width:
|
| 528 |
+
grid.append_column()
|
| 529 |
+
|
| 530 |
+
grid[i, j] = obj
|
| 531 |
+
return offset
|
| 532 |
+
|
| 533 |
+
@staticmethod
|
| 534 |
+
def _choose_target_cell(pt1, pt2, edge, obj, skeleton, grid):
|
| 535 |
+
"""
|
| 536 |
+
Given two points, ``pt1`` and ``pt2``, and the welding edge
|
| 537 |
+
``edge``, chooses one of the two points to place the opposing
|
| 538 |
+
vertex ``obj`` of the triangle. If neither of this points
|
| 539 |
+
fits, returns ``None``.
|
| 540 |
+
"""
|
| 541 |
+
pt1_empty = DiagramGrid._empty_point(pt1, grid)
|
| 542 |
+
pt2_empty = DiagramGrid._empty_point(pt2, grid)
|
| 543 |
+
|
| 544 |
+
if pt1_empty and pt2_empty:
|
| 545 |
+
# Both cells are empty. Of these two, choose that cell
|
| 546 |
+
# which will assure that a visible edge of the triangle
|
| 547 |
+
# will be drawn perpendicularly to the current welding
|
| 548 |
+
# edge.
|
| 549 |
+
|
| 550 |
+
A = grid[edge[0]]
|
| 551 |
+
|
| 552 |
+
if skeleton.get(frozenset([A, obj])):
|
| 553 |
+
return pt1
|
| 554 |
+
else:
|
| 555 |
+
return pt2
|
| 556 |
+
if pt1_empty:
|
| 557 |
+
return pt1
|
| 558 |
+
elif pt2_empty:
|
| 559 |
+
return pt2
|
| 560 |
+
else:
|
| 561 |
+
return None
|
| 562 |
+
|
| 563 |
+
@staticmethod
|
| 564 |
+
def _find_triangle_to_weld(triangles, fringe, grid):
|
| 565 |
+
"""
|
| 566 |
+
Finds, if possible, a triangle and an edge in the ``fringe`` to
|
| 567 |
+
which the triangle could be attached. Returns the tuple
|
| 568 |
+
containing the triangle and the index of the corresponding
|
| 569 |
+
edge in the ``fringe``.
|
| 570 |
+
|
| 571 |
+
This function relies on the fact that objects are unique in
|
| 572 |
+
the diagram.
|
| 573 |
+
"""
|
| 574 |
+
for triangle in triangles:
|
| 575 |
+
for (a, b) in fringe:
|
| 576 |
+
if frozenset([grid[a], grid[b]]) in triangle:
|
| 577 |
+
return (triangle, (a, b))
|
| 578 |
+
return None
|
| 579 |
+
|
| 580 |
+
@staticmethod
|
| 581 |
+
def _weld_triangle(tri, welding_edge, fringe, grid, skeleton):
|
| 582 |
+
"""
|
| 583 |
+
If possible, welds the triangle ``tri`` to ``fringe`` and
|
| 584 |
+
returns ``False``. If this method encounters a degenerate
|
| 585 |
+
situation in the fringe and corrects it such that a restart of
|
| 586 |
+
the search is required, it returns ``True`` (which means that
|
| 587 |
+
a restart in finding triangle weldings is required).
|
| 588 |
+
|
| 589 |
+
A degenerate situation is a situation when an edge listed in
|
| 590 |
+
the fringe does not belong to the visual boundary of the
|
| 591 |
+
diagram.
|
| 592 |
+
"""
|
| 593 |
+
a, b = welding_edge
|
| 594 |
+
target_cell = None
|
| 595 |
+
|
| 596 |
+
obj = DiagramGrid._other_vertex(tri, (grid[a], grid[b]))
|
| 597 |
+
|
| 598 |
+
# We now have a triangle and an edge where it can be welded to
|
| 599 |
+
# the fringe. Decide where to place the other vertex of the
|
| 600 |
+
# triangle and check for degenerate situations en route.
|
| 601 |
+
|
| 602 |
+
if (abs(a[0] - b[0]) == 1) and (abs(a[1] - b[1]) == 1):
|
| 603 |
+
# A diagonal edge.
|
| 604 |
+
target_cell = (a[0], b[1])
|
| 605 |
+
if grid[target_cell]:
|
| 606 |
+
# That cell is already occupied.
|
| 607 |
+
target_cell = (b[0], a[1])
|
| 608 |
+
|
| 609 |
+
if grid[target_cell]:
|
| 610 |
+
# Degenerate situation, this edge is not
|
| 611 |
+
# on the actual fringe. Correct the
|
| 612 |
+
# fringe and go on.
|
| 613 |
+
fringe.remove((a, b))
|
| 614 |
+
return True
|
| 615 |
+
elif a[0] == b[0]:
|
| 616 |
+
# A horizontal edge. We first attempt to build the
|
| 617 |
+
# triangle in the downward direction.
|
| 618 |
+
|
| 619 |
+
down_left = a[0] + 1, a[1]
|
| 620 |
+
down_right = a[0] + 1, b[1]
|
| 621 |
+
|
| 622 |
+
target_cell = DiagramGrid._choose_target_cell(
|
| 623 |
+
down_left, down_right, (a, b), obj, skeleton, grid)
|
| 624 |
+
|
| 625 |
+
if not target_cell:
|
| 626 |
+
# No room below this edge. Check above.
|
| 627 |
+
up_left = a[0] - 1, a[1]
|
| 628 |
+
up_right = a[0] - 1, b[1]
|
| 629 |
+
|
| 630 |
+
target_cell = DiagramGrid._choose_target_cell(
|
| 631 |
+
up_left, up_right, (a, b), obj, skeleton, grid)
|
| 632 |
+
|
| 633 |
+
if not target_cell:
|
| 634 |
+
# This edge is not in the fringe, remove it
|
| 635 |
+
# and restart.
|
| 636 |
+
fringe.remove((a, b))
|
| 637 |
+
return True
|
| 638 |
+
elif a[1] == b[1]:
|
| 639 |
+
# A vertical edge. We will attempt to place the other
|
| 640 |
+
# vertex of the triangle to the right of this edge.
|
| 641 |
+
right_up = a[0], a[1] + 1
|
| 642 |
+
right_down = b[0], a[1] + 1
|
| 643 |
+
|
| 644 |
+
target_cell = DiagramGrid._choose_target_cell(
|
| 645 |
+
right_up, right_down, (a, b), obj, skeleton, grid)
|
| 646 |
+
|
| 647 |
+
if not target_cell:
|
| 648 |
+
# No room to the left. See what's to the right.
|
| 649 |
+
left_up = a[0], a[1] - 1
|
| 650 |
+
left_down = b[0], a[1] - 1
|
| 651 |
+
|
| 652 |
+
target_cell = DiagramGrid._choose_target_cell(
|
| 653 |
+
left_up, left_down, (a, b), obj, skeleton, grid)
|
| 654 |
+
|
| 655 |
+
if not target_cell:
|
| 656 |
+
# This edge is not in the fringe, remove it
|
| 657 |
+
# and restart.
|
| 658 |
+
fringe.remove((a, b))
|
| 659 |
+
return True
|
| 660 |
+
|
| 661 |
+
# We now know where to place the other vertex of the
|
| 662 |
+
# triangle.
|
| 663 |
+
offset = DiagramGrid._put_object(target_cell, obj, grid, fringe)
|
| 664 |
+
|
| 665 |
+
# Take care of the displacement of coordinates if a row or
|
| 666 |
+
# a column was prepended.
|
| 667 |
+
target_cell = (target_cell[0] + offset[0],
|
| 668 |
+
target_cell[1] + offset[1])
|
| 669 |
+
a = (a[0] + offset[0], a[1] + offset[1])
|
| 670 |
+
b = (b[0] + offset[0], b[1] + offset[1])
|
| 671 |
+
|
| 672 |
+
fringe.extend([(a, target_cell), (b, target_cell)])
|
| 673 |
+
|
| 674 |
+
# No restart is required.
|
| 675 |
+
return False
|
| 676 |
+
|
| 677 |
+
@staticmethod
|
| 678 |
+
def _triangle_key(tri, triangle_sizes):
|
| 679 |
+
"""
|
| 680 |
+
Returns a key for the supplied triangle. It should be the
|
| 681 |
+
same independently of the hash randomisation.
|
| 682 |
+
"""
|
| 683 |
+
objects = sorted(
|
| 684 |
+
DiagramGrid._triangle_objects(tri), key=default_sort_key)
|
| 685 |
+
return (triangle_sizes[tri], default_sort_key(objects))
|
| 686 |
+
|
| 687 |
+
@staticmethod
|
| 688 |
+
def _pick_root_edge(tri, skeleton):
|
| 689 |
+
"""
|
| 690 |
+
For a given triangle always picks the same root edge. The
|
| 691 |
+
root edge is the edge that will be placed first on the grid.
|
| 692 |
+
"""
|
| 693 |
+
candidates = [sorted(e, key=default_sort_key)
|
| 694 |
+
for e in tri if skeleton[e]]
|
| 695 |
+
sorted_candidates = sorted(candidates, key=default_sort_key)
|
| 696 |
+
# Don't forget to assure the proper ordering of the vertices
|
| 697 |
+
# in this edge.
|
| 698 |
+
return tuple(sorted(sorted_candidates[0], key=default_sort_key))
|
| 699 |
+
|
| 700 |
+
@staticmethod
|
| 701 |
+
def _drop_irrelevant_triangles(triangles, placed_objects):
|
| 702 |
+
"""
|
| 703 |
+
Returns only those triangles whose set of objects is not
|
| 704 |
+
completely included in ``placed_objects``.
|
| 705 |
+
"""
|
| 706 |
+
return [tri for tri in triangles if not placed_objects.issuperset(
|
| 707 |
+
DiagramGrid._triangle_objects(tri))]
|
| 708 |
+
|
| 709 |
+
@staticmethod
|
| 710 |
+
def _grow_pseudopod(triangles, fringe, grid, skeleton, placed_objects):
|
| 711 |
+
"""
|
| 712 |
+
Starting from an object in the existing structure on the ``grid``,
|
| 713 |
+
adds an edge to which a triangle from ``triangles`` could be
|
| 714 |
+
welded. If this method has found a way to do so, it returns
|
| 715 |
+
the object it has just added.
|
| 716 |
+
|
| 717 |
+
This method should be applied when ``_weld_triangle`` cannot
|
| 718 |
+
find weldings any more.
|
| 719 |
+
"""
|
| 720 |
+
for i in range(grid.height):
|
| 721 |
+
for j in range(grid.width):
|
| 722 |
+
obj = grid[i, j]
|
| 723 |
+
if not obj:
|
| 724 |
+
continue
|
| 725 |
+
|
| 726 |
+
# Here we need to choose a triangle which has only
|
| 727 |
+
# ``obj`` in common with the existing structure. The
|
| 728 |
+
# situations when this is not possible should be
|
| 729 |
+
# handled elsewhere.
|
| 730 |
+
|
| 731 |
+
def good_triangle(tri):
|
| 732 |
+
objs = DiagramGrid._triangle_objects(tri)
|
| 733 |
+
return obj in objs and \
|
| 734 |
+
placed_objects & (objs - {obj}) == set()
|
| 735 |
+
|
| 736 |
+
tris = [tri for tri in triangles if good_triangle(tri)]
|
| 737 |
+
if not tris:
|
| 738 |
+
# This object is not interesting.
|
| 739 |
+
continue
|
| 740 |
+
|
| 741 |
+
# Pick the "simplest" of the triangles which could be
|
| 742 |
+
# attached. Remember that the list of triangles is
|
| 743 |
+
# sorted according to their "simplicity" (see
|
| 744 |
+
# _compute_triangle_min_sizes for the metric).
|
| 745 |
+
#
|
| 746 |
+
# Note that ``tris`` are sequentially built from
|
| 747 |
+
# ``triangles``, so we don't have to worry about hash
|
| 748 |
+
# randomisation.
|
| 749 |
+
tri = tris[0]
|
| 750 |
+
|
| 751 |
+
# We have found a triangle which could be attached to
|
| 752 |
+
# the existing structure by a vertex.
|
| 753 |
+
|
| 754 |
+
candidates = sorted([e for e in tri if skeleton[e]],
|
| 755 |
+
key=lambda e: FiniteSet(*e).sort_key())
|
| 756 |
+
edges = [e for e in candidates if obj in e]
|
| 757 |
+
|
| 758 |
+
# Note that a meaningful edge (i.e., and edge that is
|
| 759 |
+
# associated with a morphism) containing ``obj``
|
| 760 |
+
# always exists. That's because all triangles are
|
| 761 |
+
# guaranteed to have at least two meaningful edges.
|
| 762 |
+
# See _drop_redundant_triangles.
|
| 763 |
+
|
| 764 |
+
# Get the object at the other end of the edge.
|
| 765 |
+
edge = edges[0]
|
| 766 |
+
other_obj = tuple(edge - frozenset([obj]))[0]
|
| 767 |
+
|
| 768 |
+
# Now check for free directions. When checking for
|
| 769 |
+
# free directions, prefer the horizontal and vertical
|
| 770 |
+
# directions.
|
| 771 |
+
neighbours = [(i - 1, j), (i, j + 1), (i + 1, j), (i, j - 1),
|
| 772 |
+
(i - 1, j - 1), (i - 1, j + 1), (i + 1, j - 1), (i + 1, j + 1)]
|
| 773 |
+
|
| 774 |
+
for pt in neighbours:
|
| 775 |
+
if DiagramGrid._empty_point(pt, grid):
|
| 776 |
+
# We have a found a place to grow the
|
| 777 |
+
# pseudopod into.
|
| 778 |
+
offset = DiagramGrid._put_object(
|
| 779 |
+
pt, other_obj, grid, fringe)
|
| 780 |
+
|
| 781 |
+
i += offset[0]
|
| 782 |
+
j += offset[1]
|
| 783 |
+
pt = (pt[0] + offset[0], pt[1] + offset[1])
|
| 784 |
+
fringe.append(((i, j), pt))
|
| 785 |
+
|
| 786 |
+
return other_obj
|
| 787 |
+
|
| 788 |
+
# This diagram is actually cooler that I can handle. Fail cowardly.
|
| 789 |
+
return None
|
| 790 |
+
|
| 791 |
+
@staticmethod
|
| 792 |
+
def _handle_groups(diagram, groups, merged_morphisms, hints):
|
| 793 |
+
"""
|
| 794 |
+
Given the slightly preprocessed morphisms of the diagram,
|
| 795 |
+
produces a grid laid out according to ``groups``.
|
| 796 |
+
|
| 797 |
+
If a group has hints, it is laid out with those hints only,
|
| 798 |
+
without any influence from ``hints``. Otherwise, it is laid
|
| 799 |
+
out with ``hints``.
|
| 800 |
+
"""
|
| 801 |
+
def lay_out_group(group, local_hints):
|
| 802 |
+
"""
|
| 803 |
+
If ``group`` is a set of objects, uses a ``DiagramGrid``
|
| 804 |
+
to lay it out and returns the grid. Otherwise returns the
|
| 805 |
+
object (i.e., ``group``). If ``local_hints`` is not
|
| 806 |
+
empty, it is supplied to ``DiagramGrid`` as the dictionary
|
| 807 |
+
of hints. Otherwise, the ``hints`` argument of
|
| 808 |
+
``_handle_groups`` is used.
|
| 809 |
+
"""
|
| 810 |
+
if isinstance(group, FiniteSet):
|
| 811 |
+
# Set up the corresponding object-to-group
|
| 812 |
+
# mappings.
|
| 813 |
+
for obj in group:
|
| 814 |
+
obj_groups[obj] = group
|
| 815 |
+
|
| 816 |
+
# Lay out the current group.
|
| 817 |
+
if local_hints:
|
| 818 |
+
groups_grids[group] = DiagramGrid(
|
| 819 |
+
diagram.subdiagram_from_objects(group), **local_hints)
|
| 820 |
+
else:
|
| 821 |
+
groups_grids[group] = DiagramGrid(
|
| 822 |
+
diagram.subdiagram_from_objects(group), **hints)
|
| 823 |
+
else:
|
| 824 |
+
obj_groups[group] = group
|
| 825 |
+
|
| 826 |
+
def group_to_finiteset(group):
|
| 827 |
+
"""
|
| 828 |
+
Converts ``group`` to a :class:``FiniteSet`` if it is an
|
| 829 |
+
iterable.
|
| 830 |
+
"""
|
| 831 |
+
if iterable(group):
|
| 832 |
+
return FiniteSet(*group)
|
| 833 |
+
else:
|
| 834 |
+
return group
|
| 835 |
+
|
| 836 |
+
obj_groups = {}
|
| 837 |
+
groups_grids = {}
|
| 838 |
+
|
| 839 |
+
# We would like to support various containers to represent
|
| 840 |
+
# groups. To achieve that, before laying each group out, it
|
| 841 |
+
# should be converted to a FiniteSet, because that is what the
|
| 842 |
+
# following code expects.
|
| 843 |
+
|
| 844 |
+
if isinstance(groups, (dict, Dict)):
|
| 845 |
+
finiteset_groups = {}
|
| 846 |
+
for group, local_hints in groups.items():
|
| 847 |
+
finiteset_group = group_to_finiteset(group)
|
| 848 |
+
finiteset_groups[finiteset_group] = local_hints
|
| 849 |
+
lay_out_group(group, local_hints)
|
| 850 |
+
groups = finiteset_groups
|
| 851 |
+
else:
|
| 852 |
+
finiteset_groups = []
|
| 853 |
+
for group in groups:
|
| 854 |
+
finiteset_group = group_to_finiteset(group)
|
| 855 |
+
finiteset_groups.append(finiteset_group)
|
| 856 |
+
lay_out_group(finiteset_group, None)
|
| 857 |
+
groups = finiteset_groups
|
| 858 |
+
|
| 859 |
+
new_morphisms = []
|
| 860 |
+
for morphism in merged_morphisms:
|
| 861 |
+
dom = obj_groups[morphism.domain]
|
| 862 |
+
cod = obj_groups[morphism.codomain]
|
| 863 |
+
# Note that we are not really interested in morphisms
|
| 864 |
+
# which do not employ two different groups, because
|
| 865 |
+
# these do not influence the layout.
|
| 866 |
+
if dom != cod:
|
| 867 |
+
# These are essentially unnamed morphisms; they are
|
| 868 |
+
# not going to mess in the final layout. By giving
|
| 869 |
+
# them the same names, we avoid unnecessary
|
| 870 |
+
# duplicates.
|
| 871 |
+
new_morphisms.append(NamedMorphism(dom, cod, "dummy"))
|
| 872 |
+
|
| 873 |
+
# Lay out the new diagram. Since these are dummy morphisms,
|
| 874 |
+
# properties and conclusions are irrelevant.
|
| 875 |
+
top_grid = DiagramGrid(Diagram(new_morphisms))
|
| 876 |
+
|
| 877 |
+
# We now have to substitute the groups with the corresponding
|
| 878 |
+
# grids, laid out at the beginning of this function. Compute
|
| 879 |
+
# the size of each row and column in the grid, so that all
|
| 880 |
+
# nested grids fit.
|
| 881 |
+
|
| 882 |
+
def group_size(group):
|
| 883 |
+
"""
|
| 884 |
+
For the supplied group (or object, eventually), returns
|
| 885 |
+
the size of the cell that will hold this group (object).
|
| 886 |
+
"""
|
| 887 |
+
if group in groups_grids:
|
| 888 |
+
grid = groups_grids[group]
|
| 889 |
+
return (grid.height, grid.width)
|
| 890 |
+
else:
|
| 891 |
+
return (1, 1)
|
| 892 |
+
|
| 893 |
+
row_heights = [max(group_size(top_grid[i, j])[0]
|
| 894 |
+
for j in range(top_grid.width))
|
| 895 |
+
for i in range(top_grid.height)]
|
| 896 |
+
|
| 897 |
+
column_widths = [max(group_size(top_grid[i, j])[1]
|
| 898 |
+
for i in range(top_grid.height))
|
| 899 |
+
for j in range(top_grid.width)]
|
| 900 |
+
|
| 901 |
+
grid = _GrowableGrid(sum(column_widths), sum(row_heights))
|
| 902 |
+
|
| 903 |
+
real_row = 0
|
| 904 |
+
real_column = 0
|
| 905 |
+
for logical_row in range(top_grid.height):
|
| 906 |
+
for logical_column in range(top_grid.width):
|
| 907 |
+
obj = top_grid[logical_row, logical_column]
|
| 908 |
+
|
| 909 |
+
if obj in groups_grids:
|
| 910 |
+
# This is a group. Copy the corresponding grid in
|
| 911 |
+
# place.
|
| 912 |
+
local_grid = groups_grids[obj]
|
| 913 |
+
for i in range(local_grid.height):
|
| 914 |
+
for j in range(local_grid.width):
|
| 915 |
+
grid[real_row + i,
|
| 916 |
+
real_column + j] = local_grid[i, j]
|
| 917 |
+
else:
|
| 918 |
+
# This is an object. Just put it there.
|
| 919 |
+
grid[real_row, real_column] = obj
|
| 920 |
+
|
| 921 |
+
real_column += column_widths[logical_column]
|
| 922 |
+
real_column = 0
|
| 923 |
+
real_row += row_heights[logical_row]
|
| 924 |
+
|
| 925 |
+
return grid
|
| 926 |
+
|
| 927 |
+
@staticmethod
|
| 928 |
+
def _generic_layout(diagram, merged_morphisms):
|
| 929 |
+
"""
|
| 930 |
+
Produces the generic layout for the supplied diagram.
|
| 931 |
+
"""
|
| 932 |
+
all_objects = set(diagram.objects)
|
| 933 |
+
if len(all_objects) == 1:
|
| 934 |
+
# There only one object in the diagram, just put in on 1x1
|
| 935 |
+
# grid.
|
| 936 |
+
grid = _GrowableGrid(1, 1)
|
| 937 |
+
grid[0, 0] = tuple(all_objects)[0]
|
| 938 |
+
return grid
|
| 939 |
+
|
| 940 |
+
skeleton = DiagramGrid._build_skeleton(merged_morphisms)
|
| 941 |
+
|
| 942 |
+
grid = _GrowableGrid(2, 1)
|
| 943 |
+
|
| 944 |
+
if len(skeleton) == 1:
|
| 945 |
+
# This diagram contains only one morphism. Draw it
|
| 946 |
+
# horizontally.
|
| 947 |
+
objects = sorted(all_objects, key=default_sort_key)
|
| 948 |
+
grid[0, 0] = objects[0]
|
| 949 |
+
grid[0, 1] = objects[1]
|
| 950 |
+
|
| 951 |
+
return grid
|
| 952 |
+
|
| 953 |
+
triangles = DiagramGrid._list_triangles(skeleton)
|
| 954 |
+
triangles = DiagramGrid._drop_redundant_triangles(triangles, skeleton)
|
| 955 |
+
triangle_sizes = DiagramGrid._compute_triangle_min_sizes(
|
| 956 |
+
triangles, skeleton)
|
| 957 |
+
|
| 958 |
+
triangles = sorted(triangles, key=lambda tri:
|
| 959 |
+
DiagramGrid._triangle_key(tri, triangle_sizes))
|
| 960 |
+
|
| 961 |
+
# Place the first edge on the grid.
|
| 962 |
+
root_edge = DiagramGrid._pick_root_edge(triangles[0], skeleton)
|
| 963 |
+
grid[0, 0], grid[0, 1] = root_edge
|
| 964 |
+
fringe = [((0, 0), (0, 1))]
|
| 965 |
+
|
| 966 |
+
# Record which objects we now have on the grid.
|
| 967 |
+
placed_objects = set(root_edge)
|
| 968 |
+
|
| 969 |
+
while placed_objects != all_objects:
|
| 970 |
+
welding = DiagramGrid._find_triangle_to_weld(
|
| 971 |
+
triangles, fringe, grid)
|
| 972 |
+
|
| 973 |
+
if welding:
|
| 974 |
+
(triangle, welding_edge) = welding
|
| 975 |
+
|
| 976 |
+
restart_required = DiagramGrid._weld_triangle(
|
| 977 |
+
triangle, welding_edge, fringe, grid, skeleton)
|
| 978 |
+
if restart_required:
|
| 979 |
+
continue
|
| 980 |
+
|
| 981 |
+
placed_objects.update(
|
| 982 |
+
DiagramGrid._triangle_objects(triangle))
|
| 983 |
+
else:
|
| 984 |
+
# No more weldings found. Try to attach triangles by
|
| 985 |
+
# vertices.
|
| 986 |
+
new_obj = DiagramGrid._grow_pseudopod(
|
| 987 |
+
triangles, fringe, grid, skeleton, placed_objects)
|
| 988 |
+
|
| 989 |
+
if not new_obj:
|
| 990 |
+
# No more triangles can be attached, not even by
|
| 991 |
+
# the edge. We will set up a new diagram out of
|
| 992 |
+
# what has been left, laid it out independently,
|
| 993 |
+
# and then attach it to this one.
|
| 994 |
+
|
| 995 |
+
remaining_objects = all_objects - placed_objects
|
| 996 |
+
|
| 997 |
+
remaining_diagram = diagram.subdiagram_from_objects(
|
| 998 |
+
FiniteSet(*remaining_objects))
|
| 999 |
+
remaining_grid = DiagramGrid(remaining_diagram)
|
| 1000 |
+
|
| 1001 |
+
# Now, let's glue ``remaining_grid`` to ``grid``.
|
| 1002 |
+
final_width = grid.width + remaining_grid.width
|
| 1003 |
+
final_height = max(grid.height, remaining_grid.height)
|
| 1004 |
+
final_grid = _GrowableGrid(final_width, final_height)
|
| 1005 |
+
|
| 1006 |
+
for i in range(grid.width):
|
| 1007 |
+
for j in range(grid.height):
|
| 1008 |
+
final_grid[i, j] = grid[i, j]
|
| 1009 |
+
|
| 1010 |
+
start_j = grid.width
|
| 1011 |
+
for i in range(remaining_grid.height):
|
| 1012 |
+
for j in range(remaining_grid.width):
|
| 1013 |
+
final_grid[i, start_j + j] = remaining_grid[i, j]
|
| 1014 |
+
|
| 1015 |
+
return final_grid
|
| 1016 |
+
|
| 1017 |
+
placed_objects.add(new_obj)
|
| 1018 |
+
|
| 1019 |
+
triangles = DiagramGrid._drop_irrelevant_triangles(
|
| 1020 |
+
triangles, placed_objects)
|
| 1021 |
+
|
| 1022 |
+
return grid
|
| 1023 |
+
|
| 1024 |
+
@staticmethod
|
| 1025 |
+
def _get_undirected_graph(objects, merged_morphisms):
|
| 1026 |
+
"""
|
| 1027 |
+
Given the objects and the relevant morphisms of a diagram,
|
| 1028 |
+
returns the adjacency lists of the underlying undirected
|
| 1029 |
+
graph.
|
| 1030 |
+
"""
|
| 1031 |
+
adjlists = {}
|
| 1032 |
+
for obj in objects:
|
| 1033 |
+
adjlists[obj] = []
|
| 1034 |
+
|
| 1035 |
+
for morphism in merged_morphisms:
|
| 1036 |
+
adjlists[morphism.domain].append(morphism.codomain)
|
| 1037 |
+
adjlists[morphism.codomain].append(morphism.domain)
|
| 1038 |
+
|
| 1039 |
+
# Assure that the objects in the adjacency list are always in
|
| 1040 |
+
# the same order.
|
| 1041 |
+
for obj in adjlists.keys():
|
| 1042 |
+
adjlists[obj].sort(key=default_sort_key)
|
| 1043 |
+
|
| 1044 |
+
return adjlists
|
| 1045 |
+
|
| 1046 |
+
@staticmethod
|
| 1047 |
+
def _sequential_layout(diagram, merged_morphisms):
|
| 1048 |
+
r"""
|
| 1049 |
+
Lays out the diagram in "sequential" layout. This method
|
| 1050 |
+
will attempt to produce a result as close to a line as
|
| 1051 |
+
possible. For linear diagrams, the result will actually be a
|
| 1052 |
+
line.
|
| 1053 |
+
"""
|
| 1054 |
+
objects = diagram.objects
|
| 1055 |
+
sorted_objects = sorted(objects, key=default_sort_key)
|
| 1056 |
+
|
| 1057 |
+
# Set up the adjacency lists of the underlying undirected
|
| 1058 |
+
# graph of ``merged_morphisms``.
|
| 1059 |
+
adjlists = DiagramGrid._get_undirected_graph(objects, merged_morphisms)
|
| 1060 |
+
|
| 1061 |
+
root = min(sorted_objects, key=lambda x: len(adjlists[x]))
|
| 1062 |
+
grid = _GrowableGrid(1, 1)
|
| 1063 |
+
grid[0, 0] = root
|
| 1064 |
+
|
| 1065 |
+
placed_objects = {root}
|
| 1066 |
+
|
| 1067 |
+
def place_objects(pt, placed_objects):
|
| 1068 |
+
"""
|
| 1069 |
+
Does depth-first search in the underlying graph of the
|
| 1070 |
+
diagram and places the objects en route.
|
| 1071 |
+
"""
|
| 1072 |
+
# We will start placing new objects from here.
|
| 1073 |
+
new_pt = (pt[0], pt[1] + 1)
|
| 1074 |
+
|
| 1075 |
+
for adjacent_obj in adjlists[grid[pt]]:
|
| 1076 |
+
if adjacent_obj in placed_objects:
|
| 1077 |
+
# This object has already been placed.
|
| 1078 |
+
continue
|
| 1079 |
+
|
| 1080 |
+
DiagramGrid._put_object(new_pt, adjacent_obj, grid, [])
|
| 1081 |
+
placed_objects.add(adjacent_obj)
|
| 1082 |
+
placed_objects.update(place_objects(new_pt, placed_objects))
|
| 1083 |
+
|
| 1084 |
+
new_pt = (new_pt[0] + 1, new_pt[1])
|
| 1085 |
+
|
| 1086 |
+
return placed_objects
|
| 1087 |
+
|
| 1088 |
+
place_objects((0, 0), placed_objects)
|
| 1089 |
+
|
| 1090 |
+
return grid
|
| 1091 |
+
|
| 1092 |
+
@staticmethod
|
| 1093 |
+
def _drop_inessential_morphisms(merged_morphisms):
|
| 1094 |
+
r"""
|
| 1095 |
+
Removes those morphisms which should appear in the diagram,
|
| 1096 |
+
but which have no relevance to object layout.
|
| 1097 |
+
|
| 1098 |
+
Currently this removes "loop" morphisms: the non-identity
|
| 1099 |
+
morphisms with the same domains and codomains.
|
| 1100 |
+
"""
|
| 1101 |
+
morphisms = [m for m in merged_morphisms if m.domain != m.codomain]
|
| 1102 |
+
return morphisms
|
| 1103 |
+
|
| 1104 |
+
@staticmethod
|
| 1105 |
+
def _get_connected_components(objects, merged_morphisms):
|
| 1106 |
+
"""
|
| 1107 |
+
Given a container of morphisms, returns a list of connected
|
| 1108 |
+
components formed by these morphisms. A connected component
|
| 1109 |
+
is represented by a diagram consisting of the corresponding
|
| 1110 |
+
morphisms.
|
| 1111 |
+
"""
|
| 1112 |
+
component_index = {}
|
| 1113 |
+
for o in objects:
|
| 1114 |
+
component_index[o] = None
|
| 1115 |
+
|
| 1116 |
+
# Get the underlying undirected graph of the diagram.
|
| 1117 |
+
adjlist = DiagramGrid._get_undirected_graph(objects, merged_morphisms)
|
| 1118 |
+
|
| 1119 |
+
def traverse_component(object, current_index):
|
| 1120 |
+
"""
|
| 1121 |
+
Does a depth-first search traversal of the component
|
| 1122 |
+
containing ``object``.
|
| 1123 |
+
"""
|
| 1124 |
+
component_index[object] = current_index
|
| 1125 |
+
for o in adjlist[object]:
|
| 1126 |
+
if component_index[o] is None:
|
| 1127 |
+
traverse_component(o, current_index)
|
| 1128 |
+
|
| 1129 |
+
# Traverse all components.
|
| 1130 |
+
current_index = 0
|
| 1131 |
+
for o in adjlist:
|
| 1132 |
+
if component_index[o] is None:
|
| 1133 |
+
traverse_component(o, current_index)
|
| 1134 |
+
current_index += 1
|
| 1135 |
+
|
| 1136 |
+
# List the objects of the components.
|
| 1137 |
+
component_objects = [[] for i in range(current_index)]
|
| 1138 |
+
for o, idx in component_index.items():
|
| 1139 |
+
component_objects[idx].append(o)
|
| 1140 |
+
|
| 1141 |
+
# Finally, list the morphisms belonging to each component.
|
| 1142 |
+
#
|
| 1143 |
+
# Note: If some objects are isolated, they will not get any
|
| 1144 |
+
# morphisms at this stage, and since the layout algorithm
|
| 1145 |
+
# relies, we are essentially going to lose this object.
|
| 1146 |
+
# Therefore, check if there are isolated objects and, for each
|
| 1147 |
+
# of them, provide the trivial identity morphism. It will get
|
| 1148 |
+
# discarded later, but the object will be there.
|
| 1149 |
+
|
| 1150 |
+
component_morphisms = []
|
| 1151 |
+
for component in component_objects:
|
| 1152 |
+
current_morphisms = {}
|
| 1153 |
+
for m in merged_morphisms:
|
| 1154 |
+
if (m.domain in component) and (m.codomain in component):
|
| 1155 |
+
current_morphisms[m] = merged_morphisms[m]
|
| 1156 |
+
|
| 1157 |
+
if len(component) == 1:
|
| 1158 |
+
# Let's add an identity morphism, for the sake of
|
| 1159 |
+
# surely having morphisms in this component.
|
| 1160 |
+
current_morphisms[IdentityMorphism(component[0])] = FiniteSet()
|
| 1161 |
+
|
| 1162 |
+
component_morphisms.append(Diagram(current_morphisms))
|
| 1163 |
+
|
| 1164 |
+
return component_morphisms
|
| 1165 |
+
|
| 1166 |
+
def __init__(self, diagram, groups=None, **hints):
|
| 1167 |
+
premises = DiagramGrid._simplify_morphisms(diagram.premises)
|
| 1168 |
+
conclusions = DiagramGrid._simplify_morphisms(diagram.conclusions)
|
| 1169 |
+
all_merged_morphisms = DiagramGrid._merge_premises_conclusions(
|
| 1170 |
+
premises, conclusions)
|
| 1171 |
+
merged_morphisms = DiagramGrid._drop_inessential_morphisms(
|
| 1172 |
+
all_merged_morphisms)
|
| 1173 |
+
|
| 1174 |
+
# Store the merged morphisms for later use.
|
| 1175 |
+
self._morphisms = all_merged_morphisms
|
| 1176 |
+
|
| 1177 |
+
components = DiagramGrid._get_connected_components(
|
| 1178 |
+
diagram.objects, all_merged_morphisms)
|
| 1179 |
+
|
| 1180 |
+
if groups and (groups != diagram.objects):
|
| 1181 |
+
# Lay out the diagram according to the groups.
|
| 1182 |
+
self._grid = DiagramGrid._handle_groups(
|
| 1183 |
+
diagram, groups, merged_morphisms, hints)
|
| 1184 |
+
elif len(components) > 1:
|
| 1185 |
+
# Note that we check for connectedness _before_ checking
|
| 1186 |
+
# the layout hints because the layout strategies don't
|
| 1187 |
+
# know how to deal with disconnected diagrams.
|
| 1188 |
+
|
| 1189 |
+
# The diagram is disconnected. Lay out the components
|
| 1190 |
+
# independently.
|
| 1191 |
+
grids = []
|
| 1192 |
+
|
| 1193 |
+
# Sort the components to eventually get the grids arranged
|
| 1194 |
+
# in a fixed, hash-independent order.
|
| 1195 |
+
components = sorted(components, key=default_sort_key)
|
| 1196 |
+
|
| 1197 |
+
for component in components:
|
| 1198 |
+
grid = DiagramGrid(component, **hints)
|
| 1199 |
+
grids.append(grid)
|
| 1200 |
+
|
| 1201 |
+
# Throw the grids together, in a line.
|
| 1202 |
+
total_width = sum(g.width for g in grids)
|
| 1203 |
+
total_height = max(g.height for g in grids)
|
| 1204 |
+
|
| 1205 |
+
grid = _GrowableGrid(total_width, total_height)
|
| 1206 |
+
start_j = 0
|
| 1207 |
+
for g in grids:
|
| 1208 |
+
for i in range(g.height):
|
| 1209 |
+
for j in range(g.width):
|
| 1210 |
+
grid[i, start_j + j] = g[i, j]
|
| 1211 |
+
|
| 1212 |
+
start_j += g.width
|
| 1213 |
+
|
| 1214 |
+
self._grid = grid
|
| 1215 |
+
elif "layout" in hints:
|
| 1216 |
+
if hints["layout"] == "sequential":
|
| 1217 |
+
self._grid = DiagramGrid._sequential_layout(
|
| 1218 |
+
diagram, merged_morphisms)
|
| 1219 |
+
else:
|
| 1220 |
+
self._grid = DiagramGrid._generic_layout(diagram, merged_morphisms)
|
| 1221 |
+
|
| 1222 |
+
if hints.get("transpose"):
|
| 1223 |
+
# Transpose the resulting grid.
|
| 1224 |
+
grid = _GrowableGrid(self._grid.height, self._grid.width)
|
| 1225 |
+
for i in range(self._grid.height):
|
| 1226 |
+
for j in range(self._grid.width):
|
| 1227 |
+
grid[j, i] = self._grid[i, j]
|
| 1228 |
+
self._grid = grid
|
| 1229 |
+
|
| 1230 |
+
@property
|
| 1231 |
+
def width(self):
|
| 1232 |
+
"""
|
| 1233 |
+
Returns the number of columns in this diagram layout.
|
| 1234 |
+
|
| 1235 |
+
Examples
|
| 1236 |
+
========
|
| 1237 |
+
|
| 1238 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 1239 |
+
>>> from sympy.categories import Diagram, DiagramGrid
|
| 1240 |
+
>>> A = Object("A")
|
| 1241 |
+
>>> B = Object("B")
|
| 1242 |
+
>>> C = Object("C")
|
| 1243 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 1244 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 1245 |
+
>>> diagram = Diagram([f, g])
|
| 1246 |
+
>>> grid = DiagramGrid(diagram)
|
| 1247 |
+
>>> grid.width
|
| 1248 |
+
2
|
| 1249 |
+
|
| 1250 |
+
"""
|
| 1251 |
+
return self._grid.width
|
| 1252 |
+
|
| 1253 |
+
@property
|
| 1254 |
+
def height(self):
|
| 1255 |
+
"""
|
| 1256 |
+
Returns the number of rows in this diagram layout.
|
| 1257 |
+
|
| 1258 |
+
Examples
|
| 1259 |
+
========
|
| 1260 |
+
|
| 1261 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 1262 |
+
>>> from sympy.categories import Diagram, DiagramGrid
|
| 1263 |
+
>>> A = Object("A")
|
| 1264 |
+
>>> B = Object("B")
|
| 1265 |
+
>>> C = Object("C")
|
| 1266 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 1267 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 1268 |
+
>>> diagram = Diagram([f, g])
|
| 1269 |
+
>>> grid = DiagramGrid(diagram)
|
| 1270 |
+
>>> grid.height
|
| 1271 |
+
2
|
| 1272 |
+
|
| 1273 |
+
"""
|
| 1274 |
+
return self._grid.height
|
| 1275 |
+
|
| 1276 |
+
def __getitem__(self, i_j):
|
| 1277 |
+
"""
|
| 1278 |
+
Returns the object placed in the row ``i`` and column ``j``.
|
| 1279 |
+
The indices are 0-based.
|
| 1280 |
+
|
| 1281 |
+
Examples
|
| 1282 |
+
========
|
| 1283 |
+
|
| 1284 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 1285 |
+
>>> from sympy.categories import Diagram, DiagramGrid
|
| 1286 |
+
>>> A = Object("A")
|
| 1287 |
+
>>> B = Object("B")
|
| 1288 |
+
>>> C = Object("C")
|
| 1289 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 1290 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 1291 |
+
>>> diagram = Diagram([f, g])
|
| 1292 |
+
>>> grid = DiagramGrid(diagram)
|
| 1293 |
+
>>> (grid[0, 0], grid[0, 1])
|
| 1294 |
+
(Object("A"), Object("B"))
|
| 1295 |
+
>>> (grid[1, 0], grid[1, 1])
|
| 1296 |
+
(None, Object("C"))
|
| 1297 |
+
|
| 1298 |
+
"""
|
| 1299 |
+
i, j = i_j
|
| 1300 |
+
return self._grid[i, j]
|
| 1301 |
+
|
| 1302 |
+
@property
|
| 1303 |
+
def morphisms(self):
|
| 1304 |
+
"""
|
| 1305 |
+
Returns those morphisms (and their properties) which are
|
| 1306 |
+
sufficiently meaningful to be drawn.
|
| 1307 |
+
|
| 1308 |
+
Examples
|
| 1309 |
+
========
|
| 1310 |
+
|
| 1311 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 1312 |
+
>>> from sympy.categories import Diagram, DiagramGrid
|
| 1313 |
+
>>> A = Object("A")
|
| 1314 |
+
>>> B = Object("B")
|
| 1315 |
+
>>> C = Object("C")
|
| 1316 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 1317 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 1318 |
+
>>> diagram = Diagram([f, g])
|
| 1319 |
+
>>> grid = DiagramGrid(diagram)
|
| 1320 |
+
>>> grid.morphisms
|
| 1321 |
+
{NamedMorphism(Object("A"), Object("B"), "f"): EmptySet,
|
| 1322 |
+
NamedMorphism(Object("B"), Object("C"), "g"): EmptySet}
|
| 1323 |
+
|
| 1324 |
+
"""
|
| 1325 |
+
return self._morphisms
|
| 1326 |
+
|
| 1327 |
+
def __str__(self):
|
| 1328 |
+
"""
|
| 1329 |
+
Produces a string representation of this class.
|
| 1330 |
+
|
| 1331 |
+
This method returns a string representation of the underlying
|
| 1332 |
+
list of lists of objects.
|
| 1333 |
+
|
| 1334 |
+
Examples
|
| 1335 |
+
========
|
| 1336 |
+
|
| 1337 |
+
>>> from sympy.categories import Object, NamedMorphism
|
| 1338 |
+
>>> from sympy.categories import Diagram, DiagramGrid
|
| 1339 |
+
>>> A = Object("A")
|
| 1340 |
+
>>> B = Object("B")
|
| 1341 |
+
>>> C = Object("C")
|
| 1342 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 1343 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 1344 |
+
>>> diagram = Diagram([f, g])
|
| 1345 |
+
>>> grid = DiagramGrid(diagram)
|
| 1346 |
+
>>> print(grid)
|
| 1347 |
+
[[Object("A"), Object("B")],
|
| 1348 |
+
[None, Object("C")]]
|
| 1349 |
+
|
| 1350 |
+
"""
|
| 1351 |
+
return repr(self._grid._array)
|
| 1352 |
+
|
| 1353 |
+
|
| 1354 |
+
class ArrowStringDescription:
|
| 1355 |
+
r"""
|
| 1356 |
+
Stores the information necessary for producing an Xy-pic
|
| 1357 |
+
description of an arrow.
|
| 1358 |
+
|
| 1359 |
+
The principal goal of this class is to abstract away the string
|
| 1360 |
+
representation of an arrow and to also provide the functionality
|
| 1361 |
+
to produce the actual Xy-pic string.
|
| 1362 |
+
|
| 1363 |
+
``unit`` sets the unit which will be used to specify the amount of
|
| 1364 |
+
curving and other distances. ``horizontal_direction`` should be a
|
| 1365 |
+
string of ``"r"`` or ``"l"`` specifying the horizontal offset of the
|
| 1366 |
+
target cell of the arrow relatively to the current one.
|
| 1367 |
+
``vertical_direction`` should specify the vertical offset using a
|
| 1368 |
+
series of either ``"d"`` or ``"u"``. ``label_position`` should be
|
| 1369 |
+
either ``"^"``, ``"_"``, or ``"|"`` to specify that the label should
|
| 1370 |
+
be positioned above the arrow, below the arrow or just over the arrow,
|
| 1371 |
+
in a break. Note that the notions "above" and "below" are relative
|
| 1372 |
+
to arrow direction. ``label`` stores the morphism label.
|
| 1373 |
+
|
| 1374 |
+
This works as follows (disregard the yet unexplained arguments):
|
| 1375 |
+
|
| 1376 |
+
>>> from sympy.categories.diagram_drawing import ArrowStringDescription
|
| 1377 |
+
>>> astr = ArrowStringDescription(
|
| 1378 |
+
... unit="mm", curving=None, curving_amount=None,
|
| 1379 |
+
... looping_start=None, looping_end=None, horizontal_direction="d",
|
| 1380 |
+
... vertical_direction="r", label_position="_", label="f")
|
| 1381 |
+
>>> print(str(astr))
|
| 1382 |
+
\ar[dr]_{f}
|
| 1383 |
+
|
| 1384 |
+
``curving`` should be one of ``"^"``, ``"_"`` to specify in which
|
| 1385 |
+
direction the arrow is going to curve. ``curving_amount`` is a number
|
| 1386 |
+
describing how many ``unit``'s the morphism is going to curve:
|
| 1387 |
+
|
| 1388 |
+
>>> astr = ArrowStringDescription(
|
| 1389 |
+
... unit="mm", curving="^", curving_amount=12,
|
| 1390 |
+
... looping_start=None, looping_end=None, horizontal_direction="d",
|
| 1391 |
+
... vertical_direction="r", label_position="_", label="f")
|
| 1392 |
+
>>> print(str(astr))
|
| 1393 |
+
\ar@/^12mm/[dr]_{f}
|
| 1394 |
+
|
| 1395 |
+
``looping_start`` and ``looping_end`` are currently only used for
|
| 1396 |
+
loop morphisms, those which have the same domain and codomain.
|
| 1397 |
+
These two attributes should store a valid Xy-pic direction and
|
| 1398 |
+
specify, correspondingly, the direction the arrow gets out into
|
| 1399 |
+
and the direction the arrow gets back from:
|
| 1400 |
+
|
| 1401 |
+
>>> astr = ArrowStringDescription(
|
| 1402 |
+
... unit="mm", curving=None, curving_amount=None,
|
| 1403 |
+
... looping_start="u", looping_end="l", horizontal_direction="",
|
| 1404 |
+
... vertical_direction="", label_position="_", label="f")
|
| 1405 |
+
>>> print(str(astr))
|
| 1406 |
+
\ar@(u,l)[]_{f}
|
| 1407 |
+
|
| 1408 |
+
``label_displacement`` controls how far the arrow label is from
|
| 1409 |
+
the ends of the arrow. For example, to position the arrow label
|
| 1410 |
+
near the arrow head, use ">":
|
| 1411 |
+
|
| 1412 |
+
>>> astr = ArrowStringDescription(
|
| 1413 |
+
... unit="mm", curving="^", curving_amount=12,
|
| 1414 |
+
... looping_start=None, looping_end=None, horizontal_direction="d",
|
| 1415 |
+
... vertical_direction="r", label_position="_", label="f")
|
| 1416 |
+
>>> astr.label_displacement = ">"
|
| 1417 |
+
>>> print(str(astr))
|
| 1418 |
+
\ar@/^12mm/[dr]_>{f}
|
| 1419 |
+
|
| 1420 |
+
Finally, ``arrow_style`` is used to specify the arrow style. To
|
| 1421 |
+
get a dashed arrow, for example, use "{-->}" as arrow style:
|
| 1422 |
+
|
| 1423 |
+
>>> astr = ArrowStringDescription(
|
| 1424 |
+
... unit="mm", curving="^", curving_amount=12,
|
| 1425 |
+
... looping_start=None, looping_end=None, horizontal_direction="d",
|
| 1426 |
+
... vertical_direction="r", label_position="_", label="f")
|
| 1427 |
+
>>> astr.arrow_style = "{-->}"
|
| 1428 |
+
>>> print(str(astr))
|
| 1429 |
+
\ar@/^12mm/@{-->}[dr]_{f}
|
| 1430 |
+
|
| 1431 |
+
Notes
|
| 1432 |
+
=====
|
| 1433 |
+
|
| 1434 |
+
Instances of :class:`ArrowStringDescription` will be constructed
|
| 1435 |
+
by :class:`XypicDiagramDrawer` and provided for further use in
|
| 1436 |
+
formatters. The user is not expected to construct instances of
|
| 1437 |
+
:class:`ArrowStringDescription` themselves.
|
| 1438 |
+
|
| 1439 |
+
To be able to properly utilise this class, the reader is encouraged
|
| 1440 |
+
to checkout the Xy-pic user guide, available at [Xypic].
|
| 1441 |
+
|
| 1442 |
+
See Also
|
| 1443 |
+
========
|
| 1444 |
+
|
| 1445 |
+
XypicDiagramDrawer
|
| 1446 |
+
|
| 1447 |
+
References
|
| 1448 |
+
==========
|
| 1449 |
+
|
| 1450 |
+
.. [Xypic] https://xy-pic.sourceforge.net/
|
| 1451 |
+
"""
|
| 1452 |
+
def __init__(self, unit, curving, curving_amount, looping_start,
|
| 1453 |
+
looping_end, horizontal_direction, vertical_direction,
|
| 1454 |
+
label_position, label):
|
| 1455 |
+
self.unit = unit
|
| 1456 |
+
self.curving = curving
|
| 1457 |
+
self.curving_amount = curving_amount
|
| 1458 |
+
self.looping_start = looping_start
|
| 1459 |
+
self.looping_end = looping_end
|
| 1460 |
+
self.horizontal_direction = horizontal_direction
|
| 1461 |
+
self.vertical_direction = vertical_direction
|
| 1462 |
+
self.label_position = label_position
|
| 1463 |
+
self.label = label
|
| 1464 |
+
|
| 1465 |
+
self.label_displacement = ""
|
| 1466 |
+
self.arrow_style = ""
|
| 1467 |
+
|
| 1468 |
+
# This flag shows that the position of the label of this
|
| 1469 |
+
# morphism was set while typesetting a curved morphism and
|
| 1470 |
+
# should not be modified later.
|
| 1471 |
+
self.forced_label_position = False
|
| 1472 |
+
|
| 1473 |
+
def __str__(self):
|
| 1474 |
+
if self.curving:
|
| 1475 |
+
curving_str = "@/%s%d%s/" % (self.curving, self.curving_amount,
|
| 1476 |
+
self.unit)
|
| 1477 |
+
else:
|
| 1478 |
+
curving_str = ""
|
| 1479 |
+
|
| 1480 |
+
if self.looping_start and self.looping_end:
|
| 1481 |
+
looping_str = "@(%s,%s)" % (self.looping_start, self.looping_end)
|
| 1482 |
+
else:
|
| 1483 |
+
looping_str = ""
|
| 1484 |
+
|
| 1485 |
+
if self.arrow_style:
|
| 1486 |
+
|
| 1487 |
+
style_str = "@" + self.arrow_style
|
| 1488 |
+
else:
|
| 1489 |
+
style_str = ""
|
| 1490 |
+
|
| 1491 |
+
return "\\ar%s%s%s[%s%s]%s%s{%s}" % \
|
| 1492 |
+
(curving_str, looping_str, style_str, self.horizontal_direction,
|
| 1493 |
+
self.vertical_direction, self.label_position,
|
| 1494 |
+
self.label_displacement, self.label)
|
| 1495 |
+
|
| 1496 |
+
|
| 1497 |
+
class XypicDiagramDrawer:
|
| 1498 |
+
r"""
|
| 1499 |
+
Given a :class:`~.Diagram` and the corresponding
|
| 1500 |
+
:class:`DiagramGrid`, produces the Xy-pic representation of the
|
| 1501 |
+
diagram.
|
| 1502 |
+
|
| 1503 |
+
The most important method in this class is ``draw``. Consider the
|
| 1504 |
+
following triangle diagram:
|
| 1505 |
+
|
| 1506 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 1507 |
+
>>> from sympy.categories import DiagramGrid, XypicDiagramDrawer
|
| 1508 |
+
>>> A = Object("A")
|
| 1509 |
+
>>> B = Object("B")
|
| 1510 |
+
>>> C = Object("C")
|
| 1511 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 1512 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 1513 |
+
>>> diagram = Diagram([f, g], {g * f: "unique"})
|
| 1514 |
+
|
| 1515 |
+
To draw this diagram, its objects need to be laid out with a
|
| 1516 |
+
:class:`DiagramGrid`::
|
| 1517 |
+
|
| 1518 |
+
>>> grid = DiagramGrid(diagram)
|
| 1519 |
+
|
| 1520 |
+
Finally, the drawing:
|
| 1521 |
+
|
| 1522 |
+
>>> drawer = XypicDiagramDrawer()
|
| 1523 |
+
>>> print(drawer.draw(diagram, grid))
|
| 1524 |
+
\xymatrix{
|
| 1525 |
+
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
|
| 1526 |
+
C &
|
| 1527 |
+
}
|
| 1528 |
+
|
| 1529 |
+
For further details see the docstring of this method.
|
| 1530 |
+
|
| 1531 |
+
To control the appearance of the arrows, formatters are used. The
|
| 1532 |
+
dictionary ``arrow_formatters`` maps morphisms to formatter
|
| 1533 |
+
functions. A formatter is accepts an
|
| 1534 |
+
:class:`ArrowStringDescription` and is allowed to modify any of
|
| 1535 |
+
the arrow properties exposed thereby. For example, to have all
|
| 1536 |
+
morphisms with the property ``unique`` appear as dashed arrows,
|
| 1537 |
+
and to have their names prepended with `\exists !`, the following
|
| 1538 |
+
should be done:
|
| 1539 |
+
|
| 1540 |
+
>>> def formatter(astr):
|
| 1541 |
+
... astr.label = r"\exists !" + astr.label
|
| 1542 |
+
... astr.arrow_style = "{-->}"
|
| 1543 |
+
>>> drawer.arrow_formatters["unique"] = formatter
|
| 1544 |
+
>>> print(drawer.draw(diagram, grid))
|
| 1545 |
+
\xymatrix{
|
| 1546 |
+
A \ar@{-->}[d]_{\exists !g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
|
| 1547 |
+
C &
|
| 1548 |
+
}
|
| 1549 |
+
|
| 1550 |
+
To modify the appearance of all arrows in the diagram, set
|
| 1551 |
+
``default_arrow_formatter``. For example, to place all morphism
|
| 1552 |
+
labels a little bit farther from the arrow head so that they look
|
| 1553 |
+
more centred, do as follows:
|
| 1554 |
+
|
| 1555 |
+
>>> def default_formatter(astr):
|
| 1556 |
+
... astr.label_displacement = "(0.45)"
|
| 1557 |
+
>>> drawer.default_arrow_formatter = default_formatter
|
| 1558 |
+
>>> print(drawer.draw(diagram, grid))
|
| 1559 |
+
\xymatrix{
|
| 1560 |
+
A \ar@{-->}[d]_(0.45){\exists !g\circ f} \ar[r]^(0.45){f} & B \ar[ld]^(0.45){g} \\
|
| 1561 |
+
C &
|
| 1562 |
+
}
|
| 1563 |
+
|
| 1564 |
+
In some diagrams some morphisms are drawn as curved arrows.
|
| 1565 |
+
Consider the following diagram:
|
| 1566 |
+
|
| 1567 |
+
>>> D = Object("D")
|
| 1568 |
+
>>> E = Object("E")
|
| 1569 |
+
>>> h = NamedMorphism(D, A, "h")
|
| 1570 |
+
>>> k = NamedMorphism(D, B, "k")
|
| 1571 |
+
>>> diagram = Diagram([f, g, h, k])
|
| 1572 |
+
>>> grid = DiagramGrid(diagram)
|
| 1573 |
+
>>> drawer = XypicDiagramDrawer()
|
| 1574 |
+
>>> print(drawer.draw(diagram, grid))
|
| 1575 |
+
\xymatrix{
|
| 1576 |
+
A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_3mm/[ll]_{h} \\
|
| 1577 |
+
& C &
|
| 1578 |
+
}
|
| 1579 |
+
|
| 1580 |
+
To control how far the morphisms are curved by default, one can
|
| 1581 |
+
use the ``unit`` and ``default_curving_amount`` attributes:
|
| 1582 |
+
|
| 1583 |
+
>>> drawer.unit = "cm"
|
| 1584 |
+
>>> drawer.default_curving_amount = 1
|
| 1585 |
+
>>> print(drawer.draw(diagram, grid))
|
| 1586 |
+
\xymatrix{
|
| 1587 |
+
A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_1cm/[ll]_{h} \\
|
| 1588 |
+
& C &
|
| 1589 |
+
}
|
| 1590 |
+
|
| 1591 |
+
In some diagrams, there are multiple curved morphisms between the
|
| 1592 |
+
same two objects. To control by how much the curving changes
|
| 1593 |
+
between two such successive morphisms, use
|
| 1594 |
+
``default_curving_step``:
|
| 1595 |
+
|
| 1596 |
+
>>> drawer.default_curving_step = 1
|
| 1597 |
+
>>> h1 = NamedMorphism(A, D, "h1")
|
| 1598 |
+
>>> diagram = Diagram([f, g, h, k, h1])
|
| 1599 |
+
>>> grid = DiagramGrid(diagram)
|
| 1600 |
+
>>> print(drawer.draw(diagram, grid))
|
| 1601 |
+
\xymatrix{
|
| 1602 |
+
A \ar[r]_{f} \ar@/^1cm/[rr]^{h_{1}} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_2cm/[ll]_{h} \\
|
| 1603 |
+
& C &
|
| 1604 |
+
}
|
| 1605 |
+
|
| 1606 |
+
The default value of ``default_curving_step`` is 4 units.
|
| 1607 |
+
|
| 1608 |
+
See Also
|
| 1609 |
+
========
|
| 1610 |
+
|
| 1611 |
+
draw, ArrowStringDescription
|
| 1612 |
+
"""
|
| 1613 |
+
def __init__(self):
|
| 1614 |
+
self.unit = "mm"
|
| 1615 |
+
self.default_curving_amount = 3
|
| 1616 |
+
self.default_curving_step = 4
|
| 1617 |
+
|
| 1618 |
+
# This dictionary maps properties to the corresponding arrow
|
| 1619 |
+
# formatters.
|
| 1620 |
+
self.arrow_formatters = {}
|
| 1621 |
+
|
| 1622 |
+
# This is the default arrow formatter which will be applied to
|
| 1623 |
+
# each arrow independently of its properties.
|
| 1624 |
+
self.default_arrow_formatter = None
|
| 1625 |
+
|
| 1626 |
+
@staticmethod
|
| 1627 |
+
def _process_loop_morphism(i, j, grid, morphisms_str_info, object_coords):
|
| 1628 |
+
"""
|
| 1629 |
+
Produces the information required for constructing the string
|
| 1630 |
+
representation of a loop morphism. This function is invoked
|
| 1631 |
+
from ``_process_morphism``.
|
| 1632 |
+
|
| 1633 |
+
See Also
|
| 1634 |
+
========
|
| 1635 |
+
|
| 1636 |
+
_process_morphism
|
| 1637 |
+
"""
|
| 1638 |
+
curving = ""
|
| 1639 |
+
label_pos = "^"
|
| 1640 |
+
looping_start = ""
|
| 1641 |
+
looping_end = ""
|
| 1642 |
+
|
| 1643 |
+
# This is a loop morphism. Count how many morphisms stick
|
| 1644 |
+
# in each of the four quadrants. Note that straight
|
| 1645 |
+
# vertical and horizontal morphisms count in two quadrants
|
| 1646 |
+
# at the same time (i.e., a morphism going up counts both
|
| 1647 |
+
# in the first and the second quadrants).
|
| 1648 |
+
|
| 1649 |
+
# The usual numbering (counterclockwise) of quadrants
|
| 1650 |
+
# applies.
|
| 1651 |
+
quadrant = [0, 0, 0, 0]
|
| 1652 |
+
|
| 1653 |
+
obj = grid[i, j]
|
| 1654 |
+
|
| 1655 |
+
for m, m_str_info in morphisms_str_info.items():
|
| 1656 |
+
if (m.domain == obj) and (m.codomain == obj):
|
| 1657 |
+
# That's another loop morphism. Check how it
|
| 1658 |
+
# loops and mark the corresponding quadrants as
|
| 1659 |
+
# busy.
|
| 1660 |
+
(l_s, l_e) = (m_str_info.looping_start, m_str_info.looping_end)
|
| 1661 |
+
|
| 1662 |
+
if (l_s, l_e) == ("r", "u"):
|
| 1663 |
+
quadrant[0] += 1
|
| 1664 |
+
elif (l_s, l_e) == ("u", "l"):
|
| 1665 |
+
quadrant[1] += 1
|
| 1666 |
+
elif (l_s, l_e) == ("l", "d"):
|
| 1667 |
+
quadrant[2] += 1
|
| 1668 |
+
elif (l_s, l_e) == ("d", "r"):
|
| 1669 |
+
quadrant[3] += 1
|
| 1670 |
+
|
| 1671 |
+
continue
|
| 1672 |
+
if m.domain == obj:
|
| 1673 |
+
(end_i, end_j) = object_coords[m.codomain]
|
| 1674 |
+
goes_out = True
|
| 1675 |
+
elif m.codomain == obj:
|
| 1676 |
+
(end_i, end_j) = object_coords[m.domain]
|
| 1677 |
+
goes_out = False
|
| 1678 |
+
else:
|
| 1679 |
+
continue
|
| 1680 |
+
|
| 1681 |
+
d_i = end_i - i
|
| 1682 |
+
d_j = end_j - j
|
| 1683 |
+
m_curving = m_str_info.curving
|
| 1684 |
+
|
| 1685 |
+
if (d_i != 0) and (d_j != 0):
|
| 1686 |
+
# This is really a diagonal morphism. Detect the
|
| 1687 |
+
# quadrant.
|
| 1688 |
+
if (d_i > 0) and (d_j > 0):
|
| 1689 |
+
quadrant[0] += 1
|
| 1690 |
+
elif (d_i > 0) and (d_j < 0):
|
| 1691 |
+
quadrant[1] += 1
|
| 1692 |
+
elif (d_i < 0) and (d_j < 0):
|
| 1693 |
+
quadrant[2] += 1
|
| 1694 |
+
elif (d_i < 0) and (d_j > 0):
|
| 1695 |
+
quadrant[3] += 1
|
| 1696 |
+
elif d_i == 0:
|
| 1697 |
+
# Knowing where the other end of the morphism is
|
| 1698 |
+
# and which way it goes, we now have to decide
|
| 1699 |
+
# which quadrant is now the upper one and which is
|
| 1700 |
+
# the lower one.
|
| 1701 |
+
if d_j > 0:
|
| 1702 |
+
if goes_out:
|
| 1703 |
+
upper_quadrant = 0
|
| 1704 |
+
lower_quadrant = 3
|
| 1705 |
+
else:
|
| 1706 |
+
upper_quadrant = 3
|
| 1707 |
+
lower_quadrant = 0
|
| 1708 |
+
else:
|
| 1709 |
+
if goes_out:
|
| 1710 |
+
upper_quadrant = 2
|
| 1711 |
+
lower_quadrant = 1
|
| 1712 |
+
else:
|
| 1713 |
+
upper_quadrant = 1
|
| 1714 |
+
lower_quadrant = 2
|
| 1715 |
+
|
| 1716 |
+
if m_curving:
|
| 1717 |
+
if m_curving == "^":
|
| 1718 |
+
quadrant[upper_quadrant] += 1
|
| 1719 |
+
elif m_curving == "_":
|
| 1720 |
+
quadrant[lower_quadrant] += 1
|
| 1721 |
+
else:
|
| 1722 |
+
# This morphism counts in both upper and lower
|
| 1723 |
+
# quadrants.
|
| 1724 |
+
quadrant[upper_quadrant] += 1
|
| 1725 |
+
quadrant[lower_quadrant] += 1
|
| 1726 |
+
elif d_j == 0:
|
| 1727 |
+
# Knowing where the other end of the morphism is
|
| 1728 |
+
# and which way it goes, we now have to decide
|
| 1729 |
+
# which quadrant is now the left one and which is
|
| 1730 |
+
# the right one.
|
| 1731 |
+
if d_i < 0:
|
| 1732 |
+
if goes_out:
|
| 1733 |
+
left_quadrant = 1
|
| 1734 |
+
right_quadrant = 0
|
| 1735 |
+
else:
|
| 1736 |
+
left_quadrant = 0
|
| 1737 |
+
right_quadrant = 1
|
| 1738 |
+
else:
|
| 1739 |
+
if goes_out:
|
| 1740 |
+
left_quadrant = 3
|
| 1741 |
+
right_quadrant = 2
|
| 1742 |
+
else:
|
| 1743 |
+
left_quadrant = 2
|
| 1744 |
+
right_quadrant = 3
|
| 1745 |
+
|
| 1746 |
+
if m_curving:
|
| 1747 |
+
if m_curving == "^":
|
| 1748 |
+
quadrant[left_quadrant] += 1
|
| 1749 |
+
elif m_curving == "_":
|
| 1750 |
+
quadrant[right_quadrant] += 1
|
| 1751 |
+
else:
|
| 1752 |
+
# This morphism counts in both upper and lower
|
| 1753 |
+
# quadrants.
|
| 1754 |
+
quadrant[left_quadrant] += 1
|
| 1755 |
+
quadrant[right_quadrant] += 1
|
| 1756 |
+
|
| 1757 |
+
# Pick the freest quadrant to curve our morphism into.
|
| 1758 |
+
freest_quadrant = 0
|
| 1759 |
+
for i in range(4):
|
| 1760 |
+
if quadrant[i] < quadrant[freest_quadrant]:
|
| 1761 |
+
freest_quadrant = i
|
| 1762 |
+
|
| 1763 |
+
# Now set up proper looping.
|
| 1764 |
+
(looping_start, looping_end) = [("r", "u"), ("u", "l"), ("l", "d"),
|
| 1765 |
+
("d", "r")][freest_quadrant]
|
| 1766 |
+
|
| 1767 |
+
return (curving, label_pos, looping_start, looping_end)
|
| 1768 |
+
|
| 1769 |
+
@staticmethod
|
| 1770 |
+
def _process_horizontal_morphism(i, j, target_j, grid, morphisms_str_info,
|
| 1771 |
+
object_coords):
|
| 1772 |
+
"""
|
| 1773 |
+
Produces the information required for constructing the string
|
| 1774 |
+
representation of a horizontal morphism. This function is
|
| 1775 |
+
invoked from ``_process_morphism``.
|
| 1776 |
+
|
| 1777 |
+
See Also
|
| 1778 |
+
========
|
| 1779 |
+
|
| 1780 |
+
_process_morphism
|
| 1781 |
+
"""
|
| 1782 |
+
# The arrow is horizontal. Check if it goes from left to
|
| 1783 |
+
# right (``backwards == False``) or from right to left
|
| 1784 |
+
# (``backwards == True``).
|
| 1785 |
+
backwards = False
|
| 1786 |
+
start = j
|
| 1787 |
+
end = target_j
|
| 1788 |
+
if end < start:
|
| 1789 |
+
(start, end) = (end, start)
|
| 1790 |
+
backwards = True
|
| 1791 |
+
|
| 1792 |
+
# Let's see which objects are there between ``start`` and
|
| 1793 |
+
# ``end``, and then count how many morphisms stick out
|
| 1794 |
+
# upwards, and how many stick out downwards.
|
| 1795 |
+
#
|
| 1796 |
+
# For example, consider the situation:
|
| 1797 |
+
#
|
| 1798 |
+
# B1 C1
|
| 1799 |
+
# | |
|
| 1800 |
+
# A--B--C--D
|
| 1801 |
+
# |
|
| 1802 |
+
# B2
|
| 1803 |
+
#
|
| 1804 |
+
# Between the objects `A` and `D` there are two objects:
|
| 1805 |
+
# `B` and `C`. Further, there are two morphisms which
|
| 1806 |
+
# stick out upward (the ones between `B1` and `B` and
|
| 1807 |
+
# between `C` and `C1`) and one morphism which sticks out
|
| 1808 |
+
# downward (the one between `B and `B2`).
|
| 1809 |
+
#
|
| 1810 |
+
# We need this information to decide how to curve the
|
| 1811 |
+
# arrow between `A` and `D`. First of all, since there
|
| 1812 |
+
# are two objects between `A` and `D``, we must curve the
|
| 1813 |
+
# arrow. Then, we will have it curve downward, because
|
| 1814 |
+
# there is more space (less morphisms stick out downward
|
| 1815 |
+
# than upward).
|
| 1816 |
+
up = []
|
| 1817 |
+
down = []
|
| 1818 |
+
straight_horizontal = []
|
| 1819 |
+
for k in range(start + 1, end):
|
| 1820 |
+
obj = grid[i, k]
|
| 1821 |
+
if not obj:
|
| 1822 |
+
continue
|
| 1823 |
+
|
| 1824 |
+
for m in morphisms_str_info:
|
| 1825 |
+
if m.domain == obj:
|
| 1826 |
+
(end_i, end_j) = object_coords[m.codomain]
|
| 1827 |
+
elif m.codomain == obj:
|
| 1828 |
+
(end_i, end_j) = object_coords[m.domain]
|
| 1829 |
+
else:
|
| 1830 |
+
continue
|
| 1831 |
+
|
| 1832 |
+
if end_i > i:
|
| 1833 |
+
down.append(m)
|
| 1834 |
+
elif end_i < i:
|
| 1835 |
+
up.append(m)
|
| 1836 |
+
elif not morphisms_str_info[m].curving:
|
| 1837 |
+
# This is a straight horizontal morphism,
|
| 1838 |
+
# because it has no curving.
|
| 1839 |
+
straight_horizontal.append(m)
|
| 1840 |
+
|
| 1841 |
+
if len(up) < len(down):
|
| 1842 |
+
# More morphisms stick out downward than upward, let's
|
| 1843 |
+
# curve the morphism up.
|
| 1844 |
+
if backwards:
|
| 1845 |
+
curving = "_"
|
| 1846 |
+
label_pos = "_"
|
| 1847 |
+
else:
|
| 1848 |
+
curving = "^"
|
| 1849 |
+
label_pos = "^"
|
| 1850 |
+
|
| 1851 |
+
# Assure that the straight horizontal morphisms have
|
| 1852 |
+
# their labels on the lower side of the arrow.
|
| 1853 |
+
for m in straight_horizontal:
|
| 1854 |
+
(i1, j1) = object_coords[m.domain]
|
| 1855 |
+
(i2, j2) = object_coords[m.codomain]
|
| 1856 |
+
|
| 1857 |
+
m_str_info = morphisms_str_info[m]
|
| 1858 |
+
if j1 < j2:
|
| 1859 |
+
m_str_info.label_position = "_"
|
| 1860 |
+
else:
|
| 1861 |
+
m_str_info.label_position = "^"
|
| 1862 |
+
|
| 1863 |
+
# Don't allow any further modifications of the
|
| 1864 |
+
# position of this label.
|
| 1865 |
+
m_str_info.forced_label_position = True
|
| 1866 |
+
else:
|
| 1867 |
+
# More morphisms stick out downward than upward, let's
|
| 1868 |
+
# curve the morphism up.
|
| 1869 |
+
if backwards:
|
| 1870 |
+
curving = "^"
|
| 1871 |
+
label_pos = "^"
|
| 1872 |
+
else:
|
| 1873 |
+
curving = "_"
|
| 1874 |
+
label_pos = "_"
|
| 1875 |
+
|
| 1876 |
+
# Assure that the straight horizontal morphisms have
|
| 1877 |
+
# their labels on the upper side of the arrow.
|
| 1878 |
+
for m in straight_horizontal:
|
| 1879 |
+
(i1, j1) = object_coords[m.domain]
|
| 1880 |
+
(i2, j2) = object_coords[m.codomain]
|
| 1881 |
+
|
| 1882 |
+
m_str_info = morphisms_str_info[m]
|
| 1883 |
+
if j1 < j2:
|
| 1884 |
+
m_str_info.label_position = "^"
|
| 1885 |
+
else:
|
| 1886 |
+
m_str_info.label_position = "_"
|
| 1887 |
+
|
| 1888 |
+
# Don't allow any further modifications of the
|
| 1889 |
+
# position of this label.
|
| 1890 |
+
m_str_info.forced_label_position = True
|
| 1891 |
+
|
| 1892 |
+
return (curving, label_pos)
|
| 1893 |
+
|
| 1894 |
+
@staticmethod
|
| 1895 |
+
def _process_vertical_morphism(i, j, target_i, grid, morphisms_str_info,
|
| 1896 |
+
object_coords):
|
| 1897 |
+
"""
|
| 1898 |
+
Produces the information required for constructing the string
|
| 1899 |
+
representation of a vertical morphism. This function is
|
| 1900 |
+
invoked from ``_process_morphism``.
|
| 1901 |
+
|
| 1902 |
+
See Also
|
| 1903 |
+
========
|
| 1904 |
+
|
| 1905 |
+
_process_morphism
|
| 1906 |
+
"""
|
| 1907 |
+
# This arrow is vertical. Check if it goes from top to
|
| 1908 |
+
# bottom (``backwards == False``) or from bottom to top
|
| 1909 |
+
# (``backwards == True``).
|
| 1910 |
+
backwards = False
|
| 1911 |
+
start = i
|
| 1912 |
+
end = target_i
|
| 1913 |
+
if end < start:
|
| 1914 |
+
(start, end) = (end, start)
|
| 1915 |
+
backwards = True
|
| 1916 |
+
|
| 1917 |
+
# Let's see which objects are there between ``start`` and
|
| 1918 |
+
# ``end``, and then count how many morphisms stick out to
|
| 1919 |
+
# the left, and how many stick out to the right.
|
| 1920 |
+
#
|
| 1921 |
+
# See the corresponding comment in the previous branch of
|
| 1922 |
+
# this if-statement for more details.
|
| 1923 |
+
left = []
|
| 1924 |
+
right = []
|
| 1925 |
+
straight_vertical = []
|
| 1926 |
+
for k in range(start + 1, end):
|
| 1927 |
+
obj = grid[k, j]
|
| 1928 |
+
if not obj:
|
| 1929 |
+
continue
|
| 1930 |
+
|
| 1931 |
+
for m in morphisms_str_info:
|
| 1932 |
+
if m.domain == obj:
|
| 1933 |
+
(end_i, end_j) = object_coords[m.codomain]
|
| 1934 |
+
elif m.codomain == obj:
|
| 1935 |
+
(end_i, end_j) = object_coords[m.domain]
|
| 1936 |
+
else:
|
| 1937 |
+
continue
|
| 1938 |
+
|
| 1939 |
+
if end_j > j:
|
| 1940 |
+
right.append(m)
|
| 1941 |
+
elif end_j < j:
|
| 1942 |
+
left.append(m)
|
| 1943 |
+
elif not morphisms_str_info[m].curving:
|
| 1944 |
+
# This is a straight vertical morphism,
|
| 1945 |
+
# because it has no curving.
|
| 1946 |
+
straight_vertical.append(m)
|
| 1947 |
+
|
| 1948 |
+
if len(left) < len(right):
|
| 1949 |
+
# More morphisms stick out to the left than to the
|
| 1950 |
+
# right, let's curve the morphism to the right.
|
| 1951 |
+
if backwards:
|
| 1952 |
+
curving = "^"
|
| 1953 |
+
label_pos = "^"
|
| 1954 |
+
else:
|
| 1955 |
+
curving = "_"
|
| 1956 |
+
label_pos = "_"
|
| 1957 |
+
|
| 1958 |
+
# Assure that the straight vertical morphisms have
|
| 1959 |
+
# their labels on the left side of the arrow.
|
| 1960 |
+
for m in straight_vertical:
|
| 1961 |
+
(i1, j1) = object_coords[m.domain]
|
| 1962 |
+
(i2, j2) = object_coords[m.codomain]
|
| 1963 |
+
|
| 1964 |
+
m_str_info = morphisms_str_info[m]
|
| 1965 |
+
if i1 < i2:
|
| 1966 |
+
m_str_info.label_position = "^"
|
| 1967 |
+
else:
|
| 1968 |
+
m_str_info.label_position = "_"
|
| 1969 |
+
|
| 1970 |
+
# Don't allow any further modifications of the
|
| 1971 |
+
# position of this label.
|
| 1972 |
+
m_str_info.forced_label_position = True
|
| 1973 |
+
else:
|
| 1974 |
+
# More morphisms stick out to the right than to the
|
| 1975 |
+
# left, let's curve the morphism to the left.
|
| 1976 |
+
if backwards:
|
| 1977 |
+
curving = "_"
|
| 1978 |
+
label_pos = "_"
|
| 1979 |
+
else:
|
| 1980 |
+
curving = "^"
|
| 1981 |
+
label_pos = "^"
|
| 1982 |
+
|
| 1983 |
+
# Assure that the straight vertical morphisms have
|
| 1984 |
+
# their labels on the right side of the arrow.
|
| 1985 |
+
for m in straight_vertical:
|
| 1986 |
+
(i1, j1) = object_coords[m.domain]
|
| 1987 |
+
(i2, j2) = object_coords[m.codomain]
|
| 1988 |
+
|
| 1989 |
+
m_str_info = morphisms_str_info[m]
|
| 1990 |
+
if i1 < i2:
|
| 1991 |
+
m_str_info.label_position = "_"
|
| 1992 |
+
else:
|
| 1993 |
+
m_str_info.label_position = "^"
|
| 1994 |
+
|
| 1995 |
+
# Don't allow any further modifications of the
|
| 1996 |
+
# position of this label.
|
| 1997 |
+
m_str_info.forced_label_position = True
|
| 1998 |
+
|
| 1999 |
+
return (curving, label_pos)
|
| 2000 |
+
|
| 2001 |
+
def _process_morphism(self, diagram, grid, morphism, object_coords,
|
| 2002 |
+
morphisms, morphisms_str_info):
|
| 2003 |
+
"""
|
| 2004 |
+
Given the required information, produces the string
|
| 2005 |
+
representation of ``morphism``.
|
| 2006 |
+
"""
|
| 2007 |
+
def repeat_string_cond(times, str_gt, str_lt):
|
| 2008 |
+
"""
|
| 2009 |
+
If ``times > 0``, repeats ``str_gt`` ``times`` times.
|
| 2010 |
+
Otherwise, repeats ``str_lt`` ``-times`` times.
|
| 2011 |
+
"""
|
| 2012 |
+
if times > 0:
|
| 2013 |
+
return str_gt * times
|
| 2014 |
+
else:
|
| 2015 |
+
return str_lt * (-times)
|
| 2016 |
+
|
| 2017 |
+
def count_morphisms_undirected(A, B):
|
| 2018 |
+
"""
|
| 2019 |
+
Counts how many processed morphisms there are between the
|
| 2020 |
+
two supplied objects.
|
| 2021 |
+
"""
|
| 2022 |
+
return len([m for m in morphisms_str_info
|
| 2023 |
+
if {m.domain, m.codomain} == {A, B}])
|
| 2024 |
+
|
| 2025 |
+
def count_morphisms_filtered(dom, cod, curving):
|
| 2026 |
+
"""
|
| 2027 |
+
Counts the processed morphisms which go out of ``dom``
|
| 2028 |
+
into ``cod`` with curving ``curving``.
|
| 2029 |
+
"""
|
| 2030 |
+
return len([m for m, m_str_info in morphisms_str_info.items()
|
| 2031 |
+
if (m.domain, m.codomain) == (dom, cod) and
|
| 2032 |
+
(m_str_info.curving == curving)])
|
| 2033 |
+
|
| 2034 |
+
(i, j) = object_coords[morphism.domain]
|
| 2035 |
+
(target_i, target_j) = object_coords[morphism.codomain]
|
| 2036 |
+
|
| 2037 |
+
# We now need to determine the direction of
|
| 2038 |
+
# the arrow.
|
| 2039 |
+
delta_i = target_i - i
|
| 2040 |
+
delta_j = target_j - j
|
| 2041 |
+
vertical_direction = repeat_string_cond(delta_i,
|
| 2042 |
+
"d", "u")
|
| 2043 |
+
horizontal_direction = repeat_string_cond(delta_j,
|
| 2044 |
+
"r", "l")
|
| 2045 |
+
|
| 2046 |
+
curving = ""
|
| 2047 |
+
label_pos = "^"
|
| 2048 |
+
looping_start = ""
|
| 2049 |
+
looping_end = ""
|
| 2050 |
+
|
| 2051 |
+
if (delta_i == 0) and (delta_j == 0):
|
| 2052 |
+
# This is a loop morphism.
|
| 2053 |
+
(curving, label_pos, looping_start,
|
| 2054 |
+
looping_end) = XypicDiagramDrawer._process_loop_morphism(
|
| 2055 |
+
i, j, grid, morphisms_str_info, object_coords)
|
| 2056 |
+
elif (delta_i == 0) and (abs(j - target_j) > 1):
|
| 2057 |
+
# This is a horizontal morphism.
|
| 2058 |
+
(curving, label_pos) = XypicDiagramDrawer._process_horizontal_morphism(
|
| 2059 |
+
i, j, target_j, grid, morphisms_str_info, object_coords)
|
| 2060 |
+
elif (delta_j == 0) and (abs(i - target_i) > 1):
|
| 2061 |
+
# This is a vertical morphism.
|
| 2062 |
+
(curving, label_pos) = XypicDiagramDrawer._process_vertical_morphism(
|
| 2063 |
+
i, j, target_i, grid, morphisms_str_info, object_coords)
|
| 2064 |
+
|
| 2065 |
+
count = count_morphisms_undirected(morphism.domain, morphism.codomain)
|
| 2066 |
+
curving_amount = ""
|
| 2067 |
+
if curving:
|
| 2068 |
+
# This morphisms should be curved anyway.
|
| 2069 |
+
curving_amount = self.default_curving_amount + count * \
|
| 2070 |
+
self.default_curving_step
|
| 2071 |
+
elif count:
|
| 2072 |
+
# There are no objects between the domain and codomain of
|
| 2073 |
+
# the current morphism, but this is not there already are
|
| 2074 |
+
# some morphisms with the same domain and codomain, so we
|
| 2075 |
+
# have to curve this one.
|
| 2076 |
+
curving = "^"
|
| 2077 |
+
filtered_morphisms = count_morphisms_filtered(
|
| 2078 |
+
morphism.domain, morphism.codomain, curving)
|
| 2079 |
+
curving_amount = self.default_curving_amount + \
|
| 2080 |
+
filtered_morphisms * \
|
| 2081 |
+
self.default_curving_step
|
| 2082 |
+
|
| 2083 |
+
# Let's now get the name of the morphism.
|
| 2084 |
+
morphism_name = ""
|
| 2085 |
+
if isinstance(morphism, IdentityMorphism):
|
| 2086 |
+
morphism_name = "id_{%s}" + latex(grid[i, j])
|
| 2087 |
+
elif isinstance(morphism, CompositeMorphism):
|
| 2088 |
+
component_names = [latex(Symbol(component.name)) for
|
| 2089 |
+
component in morphism.components]
|
| 2090 |
+
component_names.reverse()
|
| 2091 |
+
morphism_name = "\\circ ".join(component_names)
|
| 2092 |
+
elif isinstance(morphism, NamedMorphism):
|
| 2093 |
+
morphism_name = latex(Symbol(morphism.name))
|
| 2094 |
+
|
| 2095 |
+
return ArrowStringDescription(
|
| 2096 |
+
self.unit, curving, curving_amount, looping_start,
|
| 2097 |
+
looping_end, horizontal_direction, vertical_direction,
|
| 2098 |
+
label_pos, morphism_name)
|
| 2099 |
+
|
| 2100 |
+
@staticmethod
|
| 2101 |
+
def _check_free_space_horizontal(dom_i, dom_j, cod_j, grid):
|
| 2102 |
+
"""
|
| 2103 |
+
For a horizontal morphism, checks whether there is free space
|
| 2104 |
+
(i.e., space not occupied by any objects) above the morphism
|
| 2105 |
+
or below it.
|
| 2106 |
+
"""
|
| 2107 |
+
if dom_j < cod_j:
|
| 2108 |
+
(start, end) = (dom_j, cod_j)
|
| 2109 |
+
backwards = False
|
| 2110 |
+
else:
|
| 2111 |
+
(start, end) = (cod_j, dom_j)
|
| 2112 |
+
backwards = True
|
| 2113 |
+
|
| 2114 |
+
# Check for free space above.
|
| 2115 |
+
if dom_i == 0:
|
| 2116 |
+
free_up = True
|
| 2117 |
+
else:
|
| 2118 |
+
free_up = all(grid[dom_i - 1, j] for j in
|
| 2119 |
+
range(start, end + 1))
|
| 2120 |
+
|
| 2121 |
+
# Check for free space below.
|
| 2122 |
+
if dom_i == grid.height - 1:
|
| 2123 |
+
free_down = True
|
| 2124 |
+
else:
|
| 2125 |
+
free_down = not any(grid[dom_i + 1, j] for j in
|
| 2126 |
+
range(start, end + 1))
|
| 2127 |
+
|
| 2128 |
+
return (free_up, free_down, backwards)
|
| 2129 |
+
|
| 2130 |
+
@staticmethod
|
| 2131 |
+
def _check_free_space_vertical(dom_i, cod_i, dom_j, grid):
|
| 2132 |
+
"""
|
| 2133 |
+
For a vertical morphism, checks whether there is free space
|
| 2134 |
+
(i.e., space not occupied by any objects) to the left of the
|
| 2135 |
+
morphism or to the right of it.
|
| 2136 |
+
"""
|
| 2137 |
+
if dom_i < cod_i:
|
| 2138 |
+
(start, end) = (dom_i, cod_i)
|
| 2139 |
+
backwards = False
|
| 2140 |
+
else:
|
| 2141 |
+
(start, end) = (cod_i, dom_i)
|
| 2142 |
+
backwards = True
|
| 2143 |
+
|
| 2144 |
+
# Check if there's space to the left.
|
| 2145 |
+
if dom_j == 0:
|
| 2146 |
+
free_left = True
|
| 2147 |
+
else:
|
| 2148 |
+
free_left = not any(grid[i, dom_j - 1] for i in
|
| 2149 |
+
range(start, end + 1))
|
| 2150 |
+
|
| 2151 |
+
if dom_j == grid.width - 1:
|
| 2152 |
+
free_right = True
|
| 2153 |
+
else:
|
| 2154 |
+
free_right = not any(grid[i, dom_j + 1] for i in
|
| 2155 |
+
range(start, end + 1))
|
| 2156 |
+
|
| 2157 |
+
return (free_left, free_right, backwards)
|
| 2158 |
+
|
| 2159 |
+
@staticmethod
|
| 2160 |
+
def _check_free_space_diagonal(dom_i, cod_i, dom_j, cod_j, grid):
|
| 2161 |
+
"""
|
| 2162 |
+
For a diagonal morphism, checks whether there is free space
|
| 2163 |
+
(i.e., space not occupied by any objects) above the morphism
|
| 2164 |
+
or below it.
|
| 2165 |
+
"""
|
| 2166 |
+
def abs_xrange(start, end):
|
| 2167 |
+
if start < end:
|
| 2168 |
+
return range(start, end + 1)
|
| 2169 |
+
else:
|
| 2170 |
+
return range(end, start + 1)
|
| 2171 |
+
|
| 2172 |
+
if dom_i < cod_i and dom_j < cod_j:
|
| 2173 |
+
# This morphism goes from top-left to
|
| 2174 |
+
# bottom-right.
|
| 2175 |
+
(start_i, start_j) = (dom_i, dom_j)
|
| 2176 |
+
(end_i, end_j) = (cod_i, cod_j)
|
| 2177 |
+
backwards = False
|
| 2178 |
+
elif dom_i > cod_i and dom_j > cod_j:
|
| 2179 |
+
# This morphism goes from bottom-right to
|
| 2180 |
+
# top-left.
|
| 2181 |
+
(start_i, start_j) = (cod_i, cod_j)
|
| 2182 |
+
(end_i, end_j) = (dom_i, dom_j)
|
| 2183 |
+
backwards = True
|
| 2184 |
+
if dom_i < cod_i and dom_j > cod_j:
|
| 2185 |
+
# This morphism goes from top-right to
|
| 2186 |
+
# bottom-left.
|
| 2187 |
+
(start_i, start_j) = (dom_i, dom_j)
|
| 2188 |
+
(end_i, end_j) = (cod_i, cod_j)
|
| 2189 |
+
backwards = True
|
| 2190 |
+
elif dom_i > cod_i and dom_j < cod_j:
|
| 2191 |
+
# This morphism goes from bottom-left to
|
| 2192 |
+
# top-right.
|
| 2193 |
+
(start_i, start_j) = (cod_i, cod_j)
|
| 2194 |
+
(end_i, end_j) = (dom_i, dom_j)
|
| 2195 |
+
backwards = False
|
| 2196 |
+
|
| 2197 |
+
# This is an attempt at a fast and furious strategy to
|
| 2198 |
+
# decide where there is free space on the two sides of
|
| 2199 |
+
# a diagonal morphism. For a diagonal morphism
|
| 2200 |
+
# starting at ``(start_i, start_j)`` and ending at
|
| 2201 |
+
# ``(end_i, end_j)`` the rectangle defined by these
|
| 2202 |
+
# two points is considered. The slope of the diagonal
|
| 2203 |
+
# ``alpha`` is then computed. Then, for every cell
|
| 2204 |
+
# ``(i, j)`` within the rectangle, the slope
|
| 2205 |
+
# ``alpha1`` of the line through ``(start_i,
|
| 2206 |
+
# start_j)`` and ``(i, j)`` is considered. If
|
| 2207 |
+
# ``alpha1`` is between 0 and ``alpha``, the point
|
| 2208 |
+
# ``(i, j)`` is above the diagonal, if ``alpha1`` is
|
| 2209 |
+
# between ``alpha`` and infinity, the point is below
|
| 2210 |
+
# the diagonal. Also note that, with some beforehand
|
| 2211 |
+
# precautions, this trick works for both the main and
|
| 2212 |
+
# the secondary diagonals of the rectangle.
|
| 2213 |
+
|
| 2214 |
+
# I have considered the possibility to only follow the
|
| 2215 |
+
# shorter diagonals immediately above and below the
|
| 2216 |
+
# main (or secondary) diagonal. This, however,
|
| 2217 |
+
# wouldn't have resulted in much performance gain or
|
| 2218 |
+
# better detection of outer edges, because of
|
| 2219 |
+
# relatively small sizes of diagram grids, while the
|
| 2220 |
+
# code would have become harder to understand.
|
| 2221 |
+
|
| 2222 |
+
alpha = float(end_i - start_i)/(end_j - start_j)
|
| 2223 |
+
free_up = True
|
| 2224 |
+
free_down = True
|
| 2225 |
+
for i in abs_xrange(start_i, end_i):
|
| 2226 |
+
if not free_up and not free_down:
|
| 2227 |
+
break
|
| 2228 |
+
|
| 2229 |
+
for j in abs_xrange(start_j, end_j):
|
| 2230 |
+
if not free_up and not free_down:
|
| 2231 |
+
break
|
| 2232 |
+
|
| 2233 |
+
if (i, j) == (start_i, start_j):
|
| 2234 |
+
continue
|
| 2235 |
+
|
| 2236 |
+
if j == start_j:
|
| 2237 |
+
alpha1 = "inf"
|
| 2238 |
+
else:
|
| 2239 |
+
alpha1 = float(i - start_i)/(j - start_j)
|
| 2240 |
+
|
| 2241 |
+
if grid[i, j]:
|
| 2242 |
+
if (alpha1 == "inf") or (abs(alpha1) > abs(alpha)):
|
| 2243 |
+
free_down = False
|
| 2244 |
+
elif abs(alpha1) < abs(alpha):
|
| 2245 |
+
free_up = False
|
| 2246 |
+
|
| 2247 |
+
return (free_up, free_down, backwards)
|
| 2248 |
+
|
| 2249 |
+
def _push_labels_out(self, morphisms_str_info, grid, object_coords):
|
| 2250 |
+
"""
|
| 2251 |
+
For all straight morphisms which form the visual boundary of
|
| 2252 |
+
the laid out diagram, puts their labels on their outer sides.
|
| 2253 |
+
"""
|
| 2254 |
+
def set_label_position(free1, free2, pos1, pos2, backwards, m_str_info):
|
| 2255 |
+
"""
|
| 2256 |
+
Given the information about room available to one side and
|
| 2257 |
+
to the other side of a morphism (``free1`` and ``free2``),
|
| 2258 |
+
sets the position of the morphism label in such a way that
|
| 2259 |
+
it is on the freer side. This latter operations involves
|
| 2260 |
+
choice between ``pos1`` and ``pos2``, taking ``backwards``
|
| 2261 |
+
in consideration.
|
| 2262 |
+
|
| 2263 |
+
Thus this function will do nothing if either both ``free1
|
| 2264 |
+
== True`` and ``free2 == True`` or both ``free1 == False``
|
| 2265 |
+
and ``free2 == False``. In either case, choosing one side
|
| 2266 |
+
over the other presents no advantage.
|
| 2267 |
+
"""
|
| 2268 |
+
if backwards:
|
| 2269 |
+
(pos1, pos2) = (pos2, pos1)
|
| 2270 |
+
|
| 2271 |
+
if free1 and not free2:
|
| 2272 |
+
m_str_info.label_position = pos1
|
| 2273 |
+
elif free2 and not free1:
|
| 2274 |
+
m_str_info.label_position = pos2
|
| 2275 |
+
|
| 2276 |
+
for m, m_str_info in morphisms_str_info.items():
|
| 2277 |
+
if m_str_info.curving or m_str_info.forced_label_position:
|
| 2278 |
+
# This is either a curved morphism, and curved
|
| 2279 |
+
# morphisms have other magic, or the position of this
|
| 2280 |
+
# label has already been fixed.
|
| 2281 |
+
continue
|
| 2282 |
+
|
| 2283 |
+
if m.domain == m.codomain:
|
| 2284 |
+
# This is a loop morphism, their labels, again have a
|
| 2285 |
+
# different magic.
|
| 2286 |
+
continue
|
| 2287 |
+
|
| 2288 |
+
(dom_i, dom_j) = object_coords[m.domain]
|
| 2289 |
+
(cod_i, cod_j) = object_coords[m.codomain]
|
| 2290 |
+
|
| 2291 |
+
if dom_i == cod_i:
|
| 2292 |
+
# Horizontal morphism.
|
| 2293 |
+
(free_up, free_down,
|
| 2294 |
+
backwards) = XypicDiagramDrawer._check_free_space_horizontal(
|
| 2295 |
+
dom_i, dom_j, cod_j, grid)
|
| 2296 |
+
|
| 2297 |
+
set_label_position(free_up, free_down, "^", "_",
|
| 2298 |
+
backwards, m_str_info)
|
| 2299 |
+
elif dom_j == cod_j:
|
| 2300 |
+
# Vertical morphism.
|
| 2301 |
+
(free_left, free_right,
|
| 2302 |
+
backwards) = XypicDiagramDrawer._check_free_space_vertical(
|
| 2303 |
+
dom_i, cod_i, dom_j, grid)
|
| 2304 |
+
|
| 2305 |
+
set_label_position(free_left, free_right, "_", "^",
|
| 2306 |
+
backwards, m_str_info)
|
| 2307 |
+
else:
|
| 2308 |
+
# A diagonal morphism.
|
| 2309 |
+
(free_up, free_down,
|
| 2310 |
+
backwards) = XypicDiagramDrawer._check_free_space_diagonal(
|
| 2311 |
+
dom_i, cod_i, dom_j, cod_j, grid)
|
| 2312 |
+
|
| 2313 |
+
set_label_position(free_up, free_down, "^", "_",
|
| 2314 |
+
backwards, m_str_info)
|
| 2315 |
+
|
| 2316 |
+
@staticmethod
|
| 2317 |
+
def _morphism_sort_key(morphism, object_coords):
|
| 2318 |
+
"""
|
| 2319 |
+
Provides a morphism sorting key such that horizontal or
|
| 2320 |
+
vertical morphisms between neighbouring objects come
|
| 2321 |
+
first, then horizontal or vertical morphisms between more
|
| 2322 |
+
far away objects, and finally, all other morphisms.
|
| 2323 |
+
"""
|
| 2324 |
+
(i, j) = object_coords[morphism.domain]
|
| 2325 |
+
(target_i, target_j) = object_coords[morphism.codomain]
|
| 2326 |
+
|
| 2327 |
+
if morphism.domain == morphism.codomain:
|
| 2328 |
+
# Loop morphisms should get after diagonal morphisms
|
| 2329 |
+
# so that the proper direction in which to curve the
|
| 2330 |
+
# loop can be determined.
|
| 2331 |
+
return (3, 0, default_sort_key(morphism))
|
| 2332 |
+
|
| 2333 |
+
if target_i == i:
|
| 2334 |
+
return (1, abs(target_j - j), default_sort_key(morphism))
|
| 2335 |
+
|
| 2336 |
+
if target_j == j:
|
| 2337 |
+
return (1, abs(target_i - i), default_sort_key(morphism))
|
| 2338 |
+
|
| 2339 |
+
# Diagonal morphism.
|
| 2340 |
+
return (2, 0, default_sort_key(morphism))
|
| 2341 |
+
|
| 2342 |
+
@staticmethod
|
| 2343 |
+
def _build_xypic_string(diagram, grid, morphisms,
|
| 2344 |
+
morphisms_str_info, diagram_format):
|
| 2345 |
+
"""
|
| 2346 |
+
Given a collection of :class:`ArrowStringDescription`
|
| 2347 |
+
describing the morphisms of a diagram and the object layout
|
| 2348 |
+
information of a diagram, produces the final Xy-pic picture.
|
| 2349 |
+
"""
|
| 2350 |
+
# Build the mapping between objects and morphisms which have
|
| 2351 |
+
# them as domains.
|
| 2352 |
+
object_morphisms = {}
|
| 2353 |
+
for obj in diagram.objects:
|
| 2354 |
+
object_morphisms[obj] = []
|
| 2355 |
+
for morphism in morphisms:
|
| 2356 |
+
object_morphisms[morphism.domain].append(morphism)
|
| 2357 |
+
|
| 2358 |
+
result = "\\xymatrix%s{\n" % diagram_format
|
| 2359 |
+
|
| 2360 |
+
for i in range(grid.height):
|
| 2361 |
+
for j in range(grid.width):
|
| 2362 |
+
obj = grid[i, j]
|
| 2363 |
+
if obj:
|
| 2364 |
+
result += latex(obj) + " "
|
| 2365 |
+
|
| 2366 |
+
morphisms_to_draw = object_morphisms[obj]
|
| 2367 |
+
for morphism in morphisms_to_draw:
|
| 2368 |
+
result += str(morphisms_str_info[morphism]) + " "
|
| 2369 |
+
|
| 2370 |
+
# Don't put the & after the last column.
|
| 2371 |
+
if j < grid.width - 1:
|
| 2372 |
+
result += "& "
|
| 2373 |
+
|
| 2374 |
+
# Don't put the line break after the last row.
|
| 2375 |
+
if i < grid.height - 1:
|
| 2376 |
+
result += "\\\\"
|
| 2377 |
+
result += "\n"
|
| 2378 |
+
|
| 2379 |
+
result += "}\n"
|
| 2380 |
+
|
| 2381 |
+
return result
|
| 2382 |
+
|
| 2383 |
+
def draw(self, diagram, grid, masked=None, diagram_format=""):
|
| 2384 |
+
r"""
|
| 2385 |
+
Returns the Xy-pic representation of ``diagram`` laid out in
|
| 2386 |
+
``grid``.
|
| 2387 |
+
|
| 2388 |
+
Consider the following simple triangle diagram.
|
| 2389 |
+
|
| 2390 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 2391 |
+
>>> from sympy.categories import DiagramGrid, XypicDiagramDrawer
|
| 2392 |
+
>>> A = Object("A")
|
| 2393 |
+
>>> B = Object("B")
|
| 2394 |
+
>>> C = Object("C")
|
| 2395 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 2396 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 2397 |
+
>>> diagram = Diagram([f, g], {g * f: "unique"})
|
| 2398 |
+
|
| 2399 |
+
To draw this diagram, its objects need to be laid out with a
|
| 2400 |
+
:class:`DiagramGrid`::
|
| 2401 |
+
|
| 2402 |
+
>>> grid = DiagramGrid(diagram)
|
| 2403 |
+
|
| 2404 |
+
Finally, the drawing:
|
| 2405 |
+
|
| 2406 |
+
>>> drawer = XypicDiagramDrawer()
|
| 2407 |
+
>>> print(drawer.draw(diagram, grid))
|
| 2408 |
+
\xymatrix{
|
| 2409 |
+
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
|
| 2410 |
+
C &
|
| 2411 |
+
}
|
| 2412 |
+
|
| 2413 |
+
The argument ``masked`` can be used to skip morphisms in the
|
| 2414 |
+
presentation of the diagram:
|
| 2415 |
+
|
| 2416 |
+
>>> print(drawer.draw(diagram, grid, masked=[g * f]))
|
| 2417 |
+
\xymatrix{
|
| 2418 |
+
A \ar[r]^{f} & B \ar[ld]^{g} \\
|
| 2419 |
+
C &
|
| 2420 |
+
}
|
| 2421 |
+
|
| 2422 |
+
Finally, the ``diagram_format`` argument can be used to
|
| 2423 |
+
specify the format string of the diagram. For example, to
|
| 2424 |
+
increase the spacing by 1 cm, proceeding as follows:
|
| 2425 |
+
|
| 2426 |
+
>>> print(drawer.draw(diagram, grid, diagram_format="@+1cm"))
|
| 2427 |
+
\xymatrix@+1cm{
|
| 2428 |
+
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
|
| 2429 |
+
C &
|
| 2430 |
+
}
|
| 2431 |
+
|
| 2432 |
+
"""
|
| 2433 |
+
# This method works in several steps. It starts by removing
|
| 2434 |
+
# the masked morphisms, if necessary, and then maps objects to
|
| 2435 |
+
# their positions in the grid (coordinate tuples). Remember
|
| 2436 |
+
# that objects are unique in ``Diagram`` and in the layout
|
| 2437 |
+
# produced by ``DiagramGrid``, so every object is mapped to a
|
| 2438 |
+
# single coordinate pair.
|
| 2439 |
+
#
|
| 2440 |
+
# The next step is the central step and is concerned with
|
| 2441 |
+
# analysing the morphisms of the diagram and deciding how to
|
| 2442 |
+
# draw them. For example, how to curve the arrows is decided
|
| 2443 |
+
# at this step. The bulk of the analysis is implemented in
|
| 2444 |
+
# ``_process_morphism``, to the result of which the
|
| 2445 |
+
# appropriate formatters are applied.
|
| 2446 |
+
#
|
| 2447 |
+
# The result of the previous step is a list of
|
| 2448 |
+
# ``ArrowStringDescription``. After the analysis and
|
| 2449 |
+
# application of formatters, some extra logic tries to assure
|
| 2450 |
+
# better positioning of morphism labels (for example, an
|
| 2451 |
+
# attempt is made to avoid the situations when arrows cross
|
| 2452 |
+
# labels). This functionality constitutes the next step and
|
| 2453 |
+
# is implemented in ``_push_labels_out``. Note that label
|
| 2454 |
+
# positions which have been set via a formatter are not
|
| 2455 |
+
# affected in this step.
|
| 2456 |
+
#
|
| 2457 |
+
# Finally, at the closing step, the array of
|
| 2458 |
+
# ``ArrowStringDescription`` and the layout information
|
| 2459 |
+
# incorporated in ``DiagramGrid`` are combined to produce the
|
| 2460 |
+
# resulting Xy-pic picture. This part of code lies in
|
| 2461 |
+
# ``_build_xypic_string``.
|
| 2462 |
+
|
| 2463 |
+
if not masked:
|
| 2464 |
+
morphisms_props = grid.morphisms
|
| 2465 |
+
else:
|
| 2466 |
+
morphisms_props = {}
|
| 2467 |
+
for m, props in grid.morphisms.items():
|
| 2468 |
+
if m in masked:
|
| 2469 |
+
continue
|
| 2470 |
+
morphisms_props[m] = props
|
| 2471 |
+
|
| 2472 |
+
# Build the mapping between objects and their position in the
|
| 2473 |
+
# grid.
|
| 2474 |
+
object_coords = {}
|
| 2475 |
+
for i in range(grid.height):
|
| 2476 |
+
for j in range(grid.width):
|
| 2477 |
+
if grid[i, j]:
|
| 2478 |
+
object_coords[grid[i, j]] = (i, j)
|
| 2479 |
+
|
| 2480 |
+
morphisms = sorted(morphisms_props,
|
| 2481 |
+
key=lambda m: XypicDiagramDrawer._morphism_sort_key(
|
| 2482 |
+
m, object_coords))
|
| 2483 |
+
|
| 2484 |
+
# Build the tuples defining the string representations of
|
| 2485 |
+
# morphisms.
|
| 2486 |
+
morphisms_str_info = {}
|
| 2487 |
+
for morphism in morphisms:
|
| 2488 |
+
string_description = self._process_morphism(
|
| 2489 |
+
diagram, grid, morphism, object_coords, morphisms,
|
| 2490 |
+
morphisms_str_info)
|
| 2491 |
+
|
| 2492 |
+
if self.default_arrow_formatter:
|
| 2493 |
+
self.default_arrow_formatter(string_description)
|
| 2494 |
+
|
| 2495 |
+
for prop in morphisms_props[morphism]:
|
| 2496 |
+
# prop is a Symbol. TODO: Find out why.
|
| 2497 |
+
if prop.name in self.arrow_formatters:
|
| 2498 |
+
formatter = self.arrow_formatters[prop.name]
|
| 2499 |
+
formatter(string_description)
|
| 2500 |
+
|
| 2501 |
+
morphisms_str_info[morphism] = string_description
|
| 2502 |
+
|
| 2503 |
+
# Reposition the labels a bit.
|
| 2504 |
+
self._push_labels_out(morphisms_str_info, grid, object_coords)
|
| 2505 |
+
|
| 2506 |
+
return XypicDiagramDrawer._build_xypic_string(
|
| 2507 |
+
diagram, grid, morphisms, morphisms_str_info, diagram_format)
|
| 2508 |
+
|
| 2509 |
+
|
| 2510 |
+
def xypic_draw_diagram(diagram, masked=None, diagram_format="",
|
| 2511 |
+
groups=None, **hints):
|
| 2512 |
+
r"""
|
| 2513 |
+
Provides a shortcut combining :class:`DiagramGrid` and
|
| 2514 |
+
:class:`XypicDiagramDrawer`. Returns an Xy-pic presentation of
|
| 2515 |
+
``diagram``. The argument ``masked`` is a list of morphisms which
|
| 2516 |
+
will be not be drawn. The argument ``diagram_format`` is the
|
| 2517 |
+
format string inserted after "\xymatrix". ``groups`` should be a
|
| 2518 |
+
set of logical groups. The ``hints`` will be passed directly to
|
| 2519 |
+
the constructor of :class:`DiagramGrid`.
|
| 2520 |
+
|
| 2521 |
+
For more information about the arguments, see the docstrings of
|
| 2522 |
+
:class:`DiagramGrid` and ``XypicDiagramDrawer.draw``.
|
| 2523 |
+
|
| 2524 |
+
Examples
|
| 2525 |
+
========
|
| 2526 |
+
|
| 2527 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 2528 |
+
>>> from sympy.categories import xypic_draw_diagram
|
| 2529 |
+
>>> A = Object("A")
|
| 2530 |
+
>>> B = Object("B")
|
| 2531 |
+
>>> C = Object("C")
|
| 2532 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 2533 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 2534 |
+
>>> diagram = Diagram([f, g], {g * f: "unique"})
|
| 2535 |
+
>>> print(xypic_draw_diagram(diagram))
|
| 2536 |
+
\xymatrix{
|
| 2537 |
+
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
|
| 2538 |
+
C &
|
| 2539 |
+
}
|
| 2540 |
+
|
| 2541 |
+
See Also
|
| 2542 |
+
========
|
| 2543 |
+
|
| 2544 |
+
XypicDiagramDrawer, DiagramGrid
|
| 2545 |
+
"""
|
| 2546 |
+
grid = DiagramGrid(diagram, groups, **hints)
|
| 2547 |
+
drawer = XypicDiagramDrawer()
|
| 2548 |
+
return drawer.draw(diagram, grid, masked, diagram_format)
|
| 2549 |
+
|
| 2550 |
+
|
| 2551 |
+
@doctest_depends_on(exe=('latex', 'dvipng'), modules=('pyglet',))
|
| 2552 |
+
def preview_diagram(diagram, masked=None, diagram_format="", groups=None,
|
| 2553 |
+
output='png', viewer=None, euler=True, **hints):
|
| 2554 |
+
"""
|
| 2555 |
+
Combines the functionality of ``xypic_draw_diagram`` and
|
| 2556 |
+
``sympy.printing.preview``. The arguments ``masked``,
|
| 2557 |
+
``diagram_format``, ``groups``, and ``hints`` are passed to
|
| 2558 |
+
``xypic_draw_diagram``, while ``output``, ``viewer, and ``euler``
|
| 2559 |
+
are passed to ``preview``.
|
| 2560 |
+
|
| 2561 |
+
Examples
|
| 2562 |
+
========
|
| 2563 |
+
|
| 2564 |
+
>>> from sympy.categories import Object, NamedMorphism, Diagram
|
| 2565 |
+
>>> from sympy.categories import preview_diagram
|
| 2566 |
+
>>> A = Object("A")
|
| 2567 |
+
>>> B = Object("B")
|
| 2568 |
+
>>> C = Object("C")
|
| 2569 |
+
>>> f = NamedMorphism(A, B, "f")
|
| 2570 |
+
>>> g = NamedMorphism(B, C, "g")
|
| 2571 |
+
>>> d = Diagram([f, g], {g * f: "unique"})
|
| 2572 |
+
>>> preview_diagram(d)
|
| 2573 |
+
|
| 2574 |
+
See Also
|
| 2575 |
+
========
|
| 2576 |
+
|
| 2577 |
+
XypicDiagramDrawer
|
| 2578 |
+
"""
|
| 2579 |
+
from sympy.printing import preview
|
| 2580 |
+
latex_output = xypic_draw_diagram(diagram, masked, diagram_format,
|
| 2581 |
+
groups, **hints)
|
| 2582 |
+
preview(latex_output, output, viewer, euler, ("xypic",))
|
lib/python3.12/site-packages/sympy/categories/tests/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/sympy/categories/tests/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (201 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/categories/tests/__pycache__/test_baseclasses.cpython-312.pyc
ADDED
|
Binary file (10.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/categories/tests/__pycache__/test_drawing.cpython-312.pyc
ADDED
|
Binary file (33.5 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/categories/tests/test_baseclasses.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.categories import (Object, Morphism, IdentityMorphism,
|
| 2 |
+
NamedMorphism, CompositeMorphism,
|
| 3 |
+
Diagram, Category)
|
| 4 |
+
from sympy.categories.baseclasses import Class
|
| 5 |
+
from sympy.testing.pytest import raises
|
| 6 |
+
from sympy.core.containers import (Dict, Tuple)
|
| 7 |
+
from sympy.sets import EmptySet
|
| 8 |
+
from sympy.sets.sets import FiniteSet
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def test_morphisms():
|
| 12 |
+
A = Object("A")
|
| 13 |
+
B = Object("B")
|
| 14 |
+
C = Object("C")
|
| 15 |
+
D = Object("D")
|
| 16 |
+
|
| 17 |
+
# Test the base morphism.
|
| 18 |
+
f = NamedMorphism(A, B, "f")
|
| 19 |
+
assert f.domain == A
|
| 20 |
+
assert f.codomain == B
|
| 21 |
+
assert f == NamedMorphism(A, B, "f")
|
| 22 |
+
|
| 23 |
+
# Test identities.
|
| 24 |
+
id_A = IdentityMorphism(A)
|
| 25 |
+
id_B = IdentityMorphism(B)
|
| 26 |
+
assert id_A.domain == A
|
| 27 |
+
assert id_A.codomain == A
|
| 28 |
+
assert id_A == IdentityMorphism(A)
|
| 29 |
+
assert id_A != id_B
|
| 30 |
+
|
| 31 |
+
# Test named morphisms.
|
| 32 |
+
g = NamedMorphism(B, C, "g")
|
| 33 |
+
assert g.name == "g"
|
| 34 |
+
assert g != f
|
| 35 |
+
assert g == NamedMorphism(B, C, "g")
|
| 36 |
+
assert g != NamedMorphism(B, C, "f")
|
| 37 |
+
|
| 38 |
+
# Test composite morphisms.
|
| 39 |
+
assert f == CompositeMorphism(f)
|
| 40 |
+
|
| 41 |
+
k = g.compose(f)
|
| 42 |
+
assert k.domain == A
|
| 43 |
+
assert k.codomain == C
|
| 44 |
+
assert k.components == Tuple(f, g)
|
| 45 |
+
assert g * f == k
|
| 46 |
+
assert CompositeMorphism(f, g) == k
|
| 47 |
+
|
| 48 |
+
assert CompositeMorphism(g * f) == g * f
|
| 49 |
+
|
| 50 |
+
# Test the associativity of composition.
|
| 51 |
+
h = NamedMorphism(C, D, "h")
|
| 52 |
+
|
| 53 |
+
p = h * g
|
| 54 |
+
u = h * g * f
|
| 55 |
+
|
| 56 |
+
assert h * k == u
|
| 57 |
+
assert p * f == u
|
| 58 |
+
assert CompositeMorphism(f, g, h) == u
|
| 59 |
+
|
| 60 |
+
# Test flattening.
|
| 61 |
+
u2 = u.flatten("u")
|
| 62 |
+
assert isinstance(u2, NamedMorphism)
|
| 63 |
+
assert u2.name == "u"
|
| 64 |
+
assert u2.domain == A
|
| 65 |
+
assert u2.codomain == D
|
| 66 |
+
|
| 67 |
+
# Test identities.
|
| 68 |
+
assert f * id_A == f
|
| 69 |
+
assert id_B * f == f
|
| 70 |
+
assert id_A * id_A == id_A
|
| 71 |
+
assert CompositeMorphism(id_A) == id_A
|
| 72 |
+
|
| 73 |
+
# Test bad compositions.
|
| 74 |
+
raises(ValueError, lambda: f * g)
|
| 75 |
+
|
| 76 |
+
raises(TypeError, lambda: f.compose(None))
|
| 77 |
+
raises(TypeError, lambda: id_A.compose(None))
|
| 78 |
+
raises(TypeError, lambda: f * None)
|
| 79 |
+
raises(TypeError, lambda: id_A * None)
|
| 80 |
+
|
| 81 |
+
raises(TypeError, lambda: CompositeMorphism(f, None, 1))
|
| 82 |
+
|
| 83 |
+
raises(ValueError, lambda: NamedMorphism(A, B, ""))
|
| 84 |
+
raises(NotImplementedError, lambda: Morphism(A, B))
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def test_diagram():
|
| 88 |
+
A = Object("A")
|
| 89 |
+
B = Object("B")
|
| 90 |
+
C = Object("C")
|
| 91 |
+
|
| 92 |
+
f = NamedMorphism(A, B, "f")
|
| 93 |
+
g = NamedMorphism(B, C, "g")
|
| 94 |
+
id_A = IdentityMorphism(A)
|
| 95 |
+
id_B = IdentityMorphism(B)
|
| 96 |
+
|
| 97 |
+
empty = EmptySet
|
| 98 |
+
|
| 99 |
+
# Test the addition of identities.
|
| 100 |
+
d1 = Diagram([f])
|
| 101 |
+
|
| 102 |
+
assert d1.objects == FiniteSet(A, B)
|
| 103 |
+
assert d1.hom(A, B) == (FiniteSet(f), empty)
|
| 104 |
+
assert d1.hom(A, A) == (FiniteSet(id_A), empty)
|
| 105 |
+
assert d1.hom(B, B) == (FiniteSet(id_B), empty)
|
| 106 |
+
|
| 107 |
+
assert d1 == Diagram([id_A, f])
|
| 108 |
+
assert d1 == Diagram([f, f])
|
| 109 |
+
|
| 110 |
+
# Test the addition of composites.
|
| 111 |
+
d2 = Diagram([f, g])
|
| 112 |
+
homAC = d2.hom(A, C)[0]
|
| 113 |
+
|
| 114 |
+
assert d2.objects == FiniteSet(A, B, C)
|
| 115 |
+
assert g * f in d2.premises.keys()
|
| 116 |
+
assert homAC == FiniteSet(g * f)
|
| 117 |
+
|
| 118 |
+
# Test equality, inequality and hash.
|
| 119 |
+
d11 = Diagram([f])
|
| 120 |
+
|
| 121 |
+
assert d1 == d11
|
| 122 |
+
assert d1 != d2
|
| 123 |
+
assert hash(d1) == hash(d11)
|
| 124 |
+
|
| 125 |
+
d11 = Diagram({f: "unique"})
|
| 126 |
+
assert d1 != d11
|
| 127 |
+
|
| 128 |
+
# Make sure that (re-)adding composites (with new properties)
|
| 129 |
+
# works as expected.
|
| 130 |
+
d = Diagram([f, g], {g * f: "unique"})
|
| 131 |
+
assert d.conclusions == Dict({g * f: FiniteSet("unique")})
|
| 132 |
+
|
| 133 |
+
# Check the hom-sets when there are premises and conclusions.
|
| 134 |
+
assert d.hom(A, C) == (FiniteSet(g * f), FiniteSet(g * f))
|
| 135 |
+
d = Diagram([f, g], [g * f])
|
| 136 |
+
assert d.hom(A, C) == (FiniteSet(g * f), FiniteSet(g * f))
|
| 137 |
+
|
| 138 |
+
# Check how the properties of composite morphisms are computed.
|
| 139 |
+
d = Diagram({f: ["unique", "isomorphism"], g: "unique"})
|
| 140 |
+
assert d.premises[g * f] == FiniteSet("unique")
|
| 141 |
+
|
| 142 |
+
# Check that conclusion morphisms with new objects are not allowed.
|
| 143 |
+
d = Diagram([f], [g])
|
| 144 |
+
assert d.conclusions == Dict({})
|
| 145 |
+
|
| 146 |
+
# Test an empty diagram.
|
| 147 |
+
d = Diagram()
|
| 148 |
+
assert d.premises == Dict({})
|
| 149 |
+
assert d.conclusions == Dict({})
|
| 150 |
+
assert d.objects == empty
|
| 151 |
+
|
| 152 |
+
# Check a SymPy Dict object.
|
| 153 |
+
d = Diagram(Dict({f: FiniteSet("unique", "isomorphism"), g: "unique"}))
|
| 154 |
+
assert d.premises[g * f] == FiniteSet("unique")
|
| 155 |
+
|
| 156 |
+
# Check the addition of components of composite morphisms.
|
| 157 |
+
d = Diagram([g * f])
|
| 158 |
+
assert f in d.premises
|
| 159 |
+
assert g in d.premises
|
| 160 |
+
|
| 161 |
+
# Check subdiagrams.
|
| 162 |
+
d = Diagram([f, g], {g * f: "unique"})
|
| 163 |
+
|
| 164 |
+
d1 = Diagram([f])
|
| 165 |
+
assert d.is_subdiagram(d1)
|
| 166 |
+
assert not d1.is_subdiagram(d)
|
| 167 |
+
|
| 168 |
+
d = Diagram([NamedMorphism(B, A, "f'")])
|
| 169 |
+
assert not d.is_subdiagram(d1)
|
| 170 |
+
assert not d1.is_subdiagram(d)
|
| 171 |
+
|
| 172 |
+
d1 = Diagram([f, g], {g * f: ["unique", "something"]})
|
| 173 |
+
assert not d.is_subdiagram(d1)
|
| 174 |
+
assert not d1.is_subdiagram(d)
|
| 175 |
+
|
| 176 |
+
d = Diagram({f: "blooh"})
|
| 177 |
+
d1 = Diagram({f: "bleeh"})
|
| 178 |
+
assert not d.is_subdiagram(d1)
|
| 179 |
+
assert not d1.is_subdiagram(d)
|
| 180 |
+
|
| 181 |
+
d = Diagram([f, g], {f: "unique", g * f: "veryunique"})
|
| 182 |
+
d1 = d.subdiagram_from_objects(FiniteSet(A, B))
|
| 183 |
+
assert d1 == Diagram([f], {f: "unique"})
|
| 184 |
+
raises(ValueError, lambda: d.subdiagram_from_objects(FiniteSet(A,
|
| 185 |
+
Object("D"))))
|
| 186 |
+
|
| 187 |
+
raises(ValueError, lambda: Diagram({IdentityMorphism(A): "unique"}))
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def test_category():
|
| 191 |
+
A = Object("A")
|
| 192 |
+
B = Object("B")
|
| 193 |
+
C = Object("C")
|
| 194 |
+
|
| 195 |
+
f = NamedMorphism(A, B, "f")
|
| 196 |
+
g = NamedMorphism(B, C, "g")
|
| 197 |
+
|
| 198 |
+
d1 = Diagram([f, g])
|
| 199 |
+
d2 = Diagram([f])
|
| 200 |
+
|
| 201 |
+
objects = d1.objects | d2.objects
|
| 202 |
+
|
| 203 |
+
K = Category("K", objects, commutative_diagrams=[d1, d2])
|
| 204 |
+
|
| 205 |
+
assert K.name == "K"
|
| 206 |
+
assert K.objects == Class(objects)
|
| 207 |
+
assert K.commutative_diagrams == FiniteSet(d1, d2)
|
| 208 |
+
|
| 209 |
+
raises(ValueError, lambda: Category(""))
|
lib/python3.12/site-packages/sympy/categories/tests/test_drawing.py
ADDED
|
@@ -0,0 +1,919 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.categories.diagram_drawing import _GrowableGrid, ArrowStringDescription
|
| 2 |
+
from sympy.categories import (DiagramGrid, Object, NamedMorphism,
|
| 3 |
+
Diagram, XypicDiagramDrawer, xypic_draw_diagram)
|
| 4 |
+
from sympy.sets.sets import FiniteSet
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def test_GrowableGrid():
|
| 8 |
+
grid = _GrowableGrid(1, 2)
|
| 9 |
+
|
| 10 |
+
# Check dimensions.
|
| 11 |
+
assert grid.width == 1
|
| 12 |
+
assert grid.height == 2
|
| 13 |
+
|
| 14 |
+
# Check initialization of elements.
|
| 15 |
+
assert grid[0, 0] is None
|
| 16 |
+
assert grid[1, 0] is None
|
| 17 |
+
|
| 18 |
+
# Check assignment to elements.
|
| 19 |
+
grid[0, 0] = 1
|
| 20 |
+
grid[1, 0] = "two"
|
| 21 |
+
|
| 22 |
+
assert grid[0, 0] == 1
|
| 23 |
+
assert grid[1, 0] == "two"
|
| 24 |
+
|
| 25 |
+
# Check appending a row.
|
| 26 |
+
grid.append_row()
|
| 27 |
+
|
| 28 |
+
assert grid.width == 1
|
| 29 |
+
assert grid.height == 3
|
| 30 |
+
|
| 31 |
+
assert grid[0, 0] == 1
|
| 32 |
+
assert grid[1, 0] == "two"
|
| 33 |
+
assert grid[2, 0] is None
|
| 34 |
+
|
| 35 |
+
# Check appending a column.
|
| 36 |
+
grid.append_column()
|
| 37 |
+
assert grid.width == 2
|
| 38 |
+
assert grid.height == 3
|
| 39 |
+
|
| 40 |
+
assert grid[0, 0] == 1
|
| 41 |
+
assert grid[1, 0] == "two"
|
| 42 |
+
assert grid[2, 0] is None
|
| 43 |
+
|
| 44 |
+
assert grid[0, 1] is None
|
| 45 |
+
assert grid[1, 1] is None
|
| 46 |
+
assert grid[2, 1] is None
|
| 47 |
+
|
| 48 |
+
grid = _GrowableGrid(1, 2)
|
| 49 |
+
grid[0, 0] = 1
|
| 50 |
+
grid[1, 0] = "two"
|
| 51 |
+
|
| 52 |
+
# Check prepending a row.
|
| 53 |
+
grid.prepend_row()
|
| 54 |
+
assert grid.width == 1
|
| 55 |
+
assert grid.height == 3
|
| 56 |
+
|
| 57 |
+
assert grid[0, 0] is None
|
| 58 |
+
assert grid[1, 0] == 1
|
| 59 |
+
assert grid[2, 0] == "two"
|
| 60 |
+
|
| 61 |
+
# Check prepending a column.
|
| 62 |
+
grid.prepend_column()
|
| 63 |
+
assert grid.width == 2
|
| 64 |
+
assert grid.height == 3
|
| 65 |
+
|
| 66 |
+
assert grid[0, 0] is None
|
| 67 |
+
assert grid[1, 0] is None
|
| 68 |
+
assert grid[2, 0] is None
|
| 69 |
+
|
| 70 |
+
assert grid[0, 1] is None
|
| 71 |
+
assert grid[1, 1] == 1
|
| 72 |
+
assert grid[2, 1] == "two"
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_DiagramGrid():
|
| 76 |
+
# Set up some objects and morphisms.
|
| 77 |
+
A = Object("A")
|
| 78 |
+
B = Object("B")
|
| 79 |
+
C = Object("C")
|
| 80 |
+
D = Object("D")
|
| 81 |
+
E = Object("E")
|
| 82 |
+
|
| 83 |
+
f = NamedMorphism(A, B, "f")
|
| 84 |
+
g = NamedMorphism(B, C, "g")
|
| 85 |
+
h = NamedMorphism(D, A, "h")
|
| 86 |
+
k = NamedMorphism(D, B, "k")
|
| 87 |
+
|
| 88 |
+
# A one-morphism diagram.
|
| 89 |
+
d = Diagram([f])
|
| 90 |
+
grid = DiagramGrid(d)
|
| 91 |
+
|
| 92 |
+
assert grid.width == 2
|
| 93 |
+
assert grid.height == 1
|
| 94 |
+
assert grid[0, 0] == A
|
| 95 |
+
assert grid[0, 1] == B
|
| 96 |
+
assert grid.morphisms == {f: FiniteSet()}
|
| 97 |
+
|
| 98 |
+
# A triangle.
|
| 99 |
+
d = Diagram([f, g], {g * f: "unique"})
|
| 100 |
+
grid = DiagramGrid(d)
|
| 101 |
+
|
| 102 |
+
assert grid.width == 2
|
| 103 |
+
assert grid.height == 2
|
| 104 |
+
assert grid[0, 0] == A
|
| 105 |
+
assert grid[0, 1] == B
|
| 106 |
+
assert grid[1, 0] == C
|
| 107 |
+
assert grid[1, 1] is None
|
| 108 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(),
|
| 109 |
+
g * f: FiniteSet("unique")}
|
| 110 |
+
|
| 111 |
+
# A triangle with a "loop" morphism.
|
| 112 |
+
l_A = NamedMorphism(A, A, "l_A")
|
| 113 |
+
d = Diagram([f, g, l_A])
|
| 114 |
+
grid = DiagramGrid(d)
|
| 115 |
+
|
| 116 |
+
assert grid.width == 2
|
| 117 |
+
assert grid.height == 2
|
| 118 |
+
assert grid[0, 0] == A
|
| 119 |
+
assert grid[0, 1] == B
|
| 120 |
+
assert grid[1, 0] is None
|
| 121 |
+
assert grid[1, 1] == C
|
| 122 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), l_A: FiniteSet()}
|
| 123 |
+
|
| 124 |
+
# A simple diagram.
|
| 125 |
+
d = Diagram([f, g, h, k])
|
| 126 |
+
grid = DiagramGrid(d)
|
| 127 |
+
|
| 128 |
+
assert grid.width == 3
|
| 129 |
+
assert grid.height == 2
|
| 130 |
+
assert grid[0, 0] == A
|
| 131 |
+
assert grid[0, 1] == B
|
| 132 |
+
assert grid[0, 2] == D
|
| 133 |
+
assert grid[1, 0] is None
|
| 134 |
+
assert grid[1, 1] == C
|
| 135 |
+
assert grid[1, 2] is None
|
| 136 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
|
| 137 |
+
k: FiniteSet()}
|
| 138 |
+
|
| 139 |
+
assert str(grid) == '[[Object("A"), Object("B"), Object("D")], ' \
|
| 140 |
+
'[None, Object("C"), None]]'
|
| 141 |
+
|
| 142 |
+
# A chain of morphisms.
|
| 143 |
+
f = NamedMorphism(A, B, "f")
|
| 144 |
+
g = NamedMorphism(B, C, "g")
|
| 145 |
+
h = NamedMorphism(C, D, "h")
|
| 146 |
+
k = NamedMorphism(D, E, "k")
|
| 147 |
+
d = Diagram([f, g, h, k])
|
| 148 |
+
grid = DiagramGrid(d)
|
| 149 |
+
|
| 150 |
+
assert grid.width == 3
|
| 151 |
+
assert grid.height == 3
|
| 152 |
+
assert grid[0, 0] == A
|
| 153 |
+
assert grid[0, 1] == B
|
| 154 |
+
assert grid[0, 2] is None
|
| 155 |
+
assert grid[1, 0] is None
|
| 156 |
+
assert grid[1, 1] == C
|
| 157 |
+
assert grid[1, 2] == D
|
| 158 |
+
assert grid[2, 0] is None
|
| 159 |
+
assert grid[2, 1] is None
|
| 160 |
+
assert grid[2, 2] == E
|
| 161 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
|
| 162 |
+
k: FiniteSet()}
|
| 163 |
+
|
| 164 |
+
# A square.
|
| 165 |
+
f = NamedMorphism(A, B, "f")
|
| 166 |
+
g = NamedMorphism(B, D, "g")
|
| 167 |
+
h = NamedMorphism(A, C, "h")
|
| 168 |
+
k = NamedMorphism(C, D, "k")
|
| 169 |
+
d = Diagram([f, g, h, k])
|
| 170 |
+
grid = DiagramGrid(d)
|
| 171 |
+
|
| 172 |
+
assert grid.width == 2
|
| 173 |
+
assert grid.height == 2
|
| 174 |
+
assert grid[0, 0] == A
|
| 175 |
+
assert grid[0, 1] == B
|
| 176 |
+
assert grid[1, 0] == C
|
| 177 |
+
assert grid[1, 1] == D
|
| 178 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
|
| 179 |
+
k: FiniteSet()}
|
| 180 |
+
|
| 181 |
+
# A strange diagram which resulted from a typo when creating a
|
| 182 |
+
# test for five lemma, but which allowed to stop one extra problem
|
| 183 |
+
# in the algorithm.
|
| 184 |
+
A = Object("A")
|
| 185 |
+
B = Object("B")
|
| 186 |
+
C = Object("C")
|
| 187 |
+
D = Object("D")
|
| 188 |
+
E = Object("E")
|
| 189 |
+
A_ = Object("A'")
|
| 190 |
+
B_ = Object("B'")
|
| 191 |
+
C_ = Object("C'")
|
| 192 |
+
D_ = Object("D'")
|
| 193 |
+
E_ = Object("E'")
|
| 194 |
+
|
| 195 |
+
f = NamedMorphism(A, B, "f")
|
| 196 |
+
g = NamedMorphism(B, C, "g")
|
| 197 |
+
h = NamedMorphism(C, D, "h")
|
| 198 |
+
i = NamedMorphism(D, E, "i")
|
| 199 |
+
|
| 200 |
+
# These 4 morphisms should be between primed objects.
|
| 201 |
+
j = NamedMorphism(A, B, "j")
|
| 202 |
+
k = NamedMorphism(B, C, "k")
|
| 203 |
+
l = NamedMorphism(C, D, "l")
|
| 204 |
+
m = NamedMorphism(D, E, "m")
|
| 205 |
+
|
| 206 |
+
o = NamedMorphism(A, A_, "o")
|
| 207 |
+
p = NamedMorphism(B, B_, "p")
|
| 208 |
+
q = NamedMorphism(C, C_, "q")
|
| 209 |
+
r = NamedMorphism(D, D_, "r")
|
| 210 |
+
s = NamedMorphism(E, E_, "s")
|
| 211 |
+
|
| 212 |
+
d = Diagram([f, g, h, i, j, k, l, m, o, p, q, r, s])
|
| 213 |
+
grid = DiagramGrid(d)
|
| 214 |
+
|
| 215 |
+
assert grid.width == 3
|
| 216 |
+
assert grid.height == 4
|
| 217 |
+
assert grid[0, 0] is None
|
| 218 |
+
assert grid[0, 1] == A
|
| 219 |
+
assert grid[0, 2] == A_
|
| 220 |
+
assert grid[1, 0] == C
|
| 221 |
+
assert grid[1, 1] == B
|
| 222 |
+
assert grid[1, 2] == B_
|
| 223 |
+
assert grid[2, 0] == C_
|
| 224 |
+
assert grid[2, 1] == D
|
| 225 |
+
assert grid[2, 2] == D_
|
| 226 |
+
assert grid[3, 0] is None
|
| 227 |
+
assert grid[3, 1] == E
|
| 228 |
+
assert grid[3, 2] == E_
|
| 229 |
+
|
| 230 |
+
morphisms = {}
|
| 231 |
+
for m in [f, g, h, i, j, k, l, m, o, p, q, r, s]:
|
| 232 |
+
morphisms[m] = FiniteSet()
|
| 233 |
+
assert grid.morphisms == morphisms
|
| 234 |
+
|
| 235 |
+
# A cube.
|
| 236 |
+
A1 = Object("A1")
|
| 237 |
+
A2 = Object("A2")
|
| 238 |
+
A3 = Object("A3")
|
| 239 |
+
A4 = Object("A4")
|
| 240 |
+
A5 = Object("A5")
|
| 241 |
+
A6 = Object("A6")
|
| 242 |
+
A7 = Object("A7")
|
| 243 |
+
A8 = Object("A8")
|
| 244 |
+
|
| 245 |
+
# The top face of the cube.
|
| 246 |
+
f1 = NamedMorphism(A1, A2, "f1")
|
| 247 |
+
f2 = NamedMorphism(A1, A3, "f2")
|
| 248 |
+
f3 = NamedMorphism(A2, A4, "f3")
|
| 249 |
+
f4 = NamedMorphism(A3, A4, "f3")
|
| 250 |
+
|
| 251 |
+
# The bottom face of the cube.
|
| 252 |
+
f5 = NamedMorphism(A5, A6, "f5")
|
| 253 |
+
f6 = NamedMorphism(A5, A7, "f6")
|
| 254 |
+
f7 = NamedMorphism(A6, A8, "f7")
|
| 255 |
+
f8 = NamedMorphism(A7, A8, "f8")
|
| 256 |
+
|
| 257 |
+
# The remaining morphisms.
|
| 258 |
+
f9 = NamedMorphism(A1, A5, "f9")
|
| 259 |
+
f10 = NamedMorphism(A2, A6, "f10")
|
| 260 |
+
f11 = NamedMorphism(A3, A7, "f11")
|
| 261 |
+
f12 = NamedMorphism(A4, A8, "f11")
|
| 262 |
+
|
| 263 |
+
d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12])
|
| 264 |
+
grid = DiagramGrid(d)
|
| 265 |
+
|
| 266 |
+
assert grid.width == 4
|
| 267 |
+
assert grid.height == 3
|
| 268 |
+
assert grid[0, 0] is None
|
| 269 |
+
assert grid[0, 1] == A5
|
| 270 |
+
assert grid[0, 2] == A6
|
| 271 |
+
assert grid[0, 3] is None
|
| 272 |
+
assert grid[1, 0] is None
|
| 273 |
+
assert grid[1, 1] == A1
|
| 274 |
+
assert grid[1, 2] == A2
|
| 275 |
+
assert grid[1, 3] is None
|
| 276 |
+
assert grid[2, 0] == A7
|
| 277 |
+
assert grid[2, 1] == A3
|
| 278 |
+
assert grid[2, 2] == A4
|
| 279 |
+
assert grid[2, 3] == A8
|
| 280 |
+
|
| 281 |
+
morphisms = {}
|
| 282 |
+
for m in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12]:
|
| 283 |
+
morphisms[m] = FiniteSet()
|
| 284 |
+
assert grid.morphisms == morphisms
|
| 285 |
+
|
| 286 |
+
# A line diagram.
|
| 287 |
+
A = Object("A")
|
| 288 |
+
B = Object("B")
|
| 289 |
+
C = Object("C")
|
| 290 |
+
D = Object("D")
|
| 291 |
+
E = Object("E")
|
| 292 |
+
|
| 293 |
+
f = NamedMorphism(A, B, "f")
|
| 294 |
+
g = NamedMorphism(B, C, "g")
|
| 295 |
+
h = NamedMorphism(C, D, "h")
|
| 296 |
+
i = NamedMorphism(D, E, "i")
|
| 297 |
+
d = Diagram([f, g, h, i])
|
| 298 |
+
grid = DiagramGrid(d, layout="sequential")
|
| 299 |
+
|
| 300 |
+
assert grid.width == 5
|
| 301 |
+
assert grid.height == 1
|
| 302 |
+
assert grid[0, 0] == A
|
| 303 |
+
assert grid[0, 1] == B
|
| 304 |
+
assert grid[0, 2] == C
|
| 305 |
+
assert grid[0, 3] == D
|
| 306 |
+
assert grid[0, 4] == E
|
| 307 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
|
| 308 |
+
i: FiniteSet()}
|
| 309 |
+
|
| 310 |
+
# Test the transposed version.
|
| 311 |
+
grid = DiagramGrid(d, layout="sequential", transpose=True)
|
| 312 |
+
|
| 313 |
+
assert grid.width == 1
|
| 314 |
+
assert grid.height == 5
|
| 315 |
+
assert grid[0, 0] == A
|
| 316 |
+
assert grid[1, 0] == B
|
| 317 |
+
assert grid[2, 0] == C
|
| 318 |
+
assert grid[3, 0] == D
|
| 319 |
+
assert grid[4, 0] == E
|
| 320 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
|
| 321 |
+
i: FiniteSet()}
|
| 322 |
+
|
| 323 |
+
# A pullback.
|
| 324 |
+
m1 = NamedMorphism(A, B, "m1")
|
| 325 |
+
m2 = NamedMorphism(A, C, "m2")
|
| 326 |
+
s1 = NamedMorphism(B, D, "s1")
|
| 327 |
+
s2 = NamedMorphism(C, D, "s2")
|
| 328 |
+
f1 = NamedMorphism(E, B, "f1")
|
| 329 |
+
f2 = NamedMorphism(E, C, "f2")
|
| 330 |
+
g = NamedMorphism(E, A, "g")
|
| 331 |
+
|
| 332 |
+
d = Diagram([m1, m2, s1, s2, f1, f2], {g: "unique"})
|
| 333 |
+
grid = DiagramGrid(d)
|
| 334 |
+
|
| 335 |
+
assert grid.width == 3
|
| 336 |
+
assert grid.height == 2
|
| 337 |
+
assert grid[0, 0] == A
|
| 338 |
+
assert grid[0, 1] == B
|
| 339 |
+
assert grid[0, 2] == E
|
| 340 |
+
assert grid[1, 0] == C
|
| 341 |
+
assert grid[1, 1] == D
|
| 342 |
+
assert grid[1, 2] is None
|
| 343 |
+
|
| 344 |
+
morphisms = {g: FiniteSet("unique")}
|
| 345 |
+
for m in [m1, m2, s1, s2, f1, f2]:
|
| 346 |
+
morphisms[m] = FiniteSet()
|
| 347 |
+
assert grid.morphisms == morphisms
|
| 348 |
+
|
| 349 |
+
# Test the pullback with sequential layout, just for stress
|
| 350 |
+
# testing.
|
| 351 |
+
grid = DiagramGrid(d, layout="sequential")
|
| 352 |
+
|
| 353 |
+
assert grid.width == 5
|
| 354 |
+
assert grid.height == 1
|
| 355 |
+
assert grid[0, 0] == D
|
| 356 |
+
assert grid[0, 1] == B
|
| 357 |
+
assert grid[0, 2] == A
|
| 358 |
+
assert grid[0, 3] == C
|
| 359 |
+
assert grid[0, 4] == E
|
| 360 |
+
assert grid.morphisms == morphisms
|
| 361 |
+
|
| 362 |
+
# Test a pullback with object grouping.
|
| 363 |
+
grid = DiagramGrid(d, groups=FiniteSet(E, FiniteSet(A, B, C, D)))
|
| 364 |
+
|
| 365 |
+
assert grid.width == 3
|
| 366 |
+
assert grid.height == 2
|
| 367 |
+
assert grid[0, 0] == E
|
| 368 |
+
assert grid[0, 1] == A
|
| 369 |
+
assert grid[0, 2] == B
|
| 370 |
+
assert grid[1, 0] is None
|
| 371 |
+
assert grid[1, 1] == C
|
| 372 |
+
assert grid[1, 2] == D
|
| 373 |
+
assert grid.morphisms == morphisms
|
| 374 |
+
|
| 375 |
+
# Five lemma, actually.
|
| 376 |
+
A = Object("A")
|
| 377 |
+
B = Object("B")
|
| 378 |
+
C = Object("C")
|
| 379 |
+
D = Object("D")
|
| 380 |
+
E = Object("E")
|
| 381 |
+
A_ = Object("A'")
|
| 382 |
+
B_ = Object("B'")
|
| 383 |
+
C_ = Object("C'")
|
| 384 |
+
D_ = Object("D'")
|
| 385 |
+
E_ = Object("E'")
|
| 386 |
+
|
| 387 |
+
f = NamedMorphism(A, B, "f")
|
| 388 |
+
g = NamedMorphism(B, C, "g")
|
| 389 |
+
h = NamedMorphism(C, D, "h")
|
| 390 |
+
i = NamedMorphism(D, E, "i")
|
| 391 |
+
|
| 392 |
+
j = NamedMorphism(A_, B_, "j")
|
| 393 |
+
k = NamedMorphism(B_, C_, "k")
|
| 394 |
+
l = NamedMorphism(C_, D_, "l")
|
| 395 |
+
m = NamedMorphism(D_, E_, "m")
|
| 396 |
+
|
| 397 |
+
o = NamedMorphism(A, A_, "o")
|
| 398 |
+
p = NamedMorphism(B, B_, "p")
|
| 399 |
+
q = NamedMorphism(C, C_, "q")
|
| 400 |
+
r = NamedMorphism(D, D_, "r")
|
| 401 |
+
s = NamedMorphism(E, E_, "s")
|
| 402 |
+
|
| 403 |
+
d = Diagram([f, g, h, i, j, k, l, m, o, p, q, r, s])
|
| 404 |
+
grid = DiagramGrid(d)
|
| 405 |
+
|
| 406 |
+
assert grid.width == 5
|
| 407 |
+
assert grid.height == 3
|
| 408 |
+
assert grid[0, 0] is None
|
| 409 |
+
assert grid[0, 1] == A
|
| 410 |
+
assert grid[0, 2] == A_
|
| 411 |
+
assert grid[0, 3] is None
|
| 412 |
+
assert grid[0, 4] is None
|
| 413 |
+
assert grid[1, 0] == C
|
| 414 |
+
assert grid[1, 1] == B
|
| 415 |
+
assert grid[1, 2] == B_
|
| 416 |
+
assert grid[1, 3] == C_
|
| 417 |
+
assert grid[1, 4] is None
|
| 418 |
+
assert grid[2, 0] == D
|
| 419 |
+
assert grid[2, 1] == E
|
| 420 |
+
assert grid[2, 2] is None
|
| 421 |
+
assert grid[2, 3] == D_
|
| 422 |
+
assert grid[2, 4] == E_
|
| 423 |
+
|
| 424 |
+
morphisms = {}
|
| 425 |
+
for m in [f, g, h, i, j, k, l, m, o, p, q, r, s]:
|
| 426 |
+
morphisms[m] = FiniteSet()
|
| 427 |
+
assert grid.morphisms == morphisms
|
| 428 |
+
|
| 429 |
+
# Test the five lemma with object grouping.
|
| 430 |
+
grid = DiagramGrid(d, FiniteSet(
|
| 431 |
+
FiniteSet(A, B, C, D, E), FiniteSet(A_, B_, C_, D_, E_)))
|
| 432 |
+
|
| 433 |
+
assert grid.width == 6
|
| 434 |
+
assert grid.height == 3
|
| 435 |
+
assert grid[0, 0] == A
|
| 436 |
+
assert grid[0, 1] == B
|
| 437 |
+
assert grid[0, 2] is None
|
| 438 |
+
assert grid[0, 3] == A_
|
| 439 |
+
assert grid[0, 4] == B_
|
| 440 |
+
assert grid[0, 5] is None
|
| 441 |
+
assert grid[1, 0] is None
|
| 442 |
+
assert grid[1, 1] == C
|
| 443 |
+
assert grid[1, 2] == D
|
| 444 |
+
assert grid[1, 3] is None
|
| 445 |
+
assert grid[1, 4] == C_
|
| 446 |
+
assert grid[1, 5] == D_
|
| 447 |
+
assert grid[2, 0] is None
|
| 448 |
+
assert grid[2, 1] is None
|
| 449 |
+
assert grid[2, 2] == E
|
| 450 |
+
assert grid[2, 3] is None
|
| 451 |
+
assert grid[2, 4] is None
|
| 452 |
+
assert grid[2, 5] == E_
|
| 453 |
+
assert grid.morphisms == morphisms
|
| 454 |
+
|
| 455 |
+
# Test the five lemma with object grouping, but mixing containers
|
| 456 |
+
# to represent groups.
|
| 457 |
+
grid = DiagramGrid(d, [(A, B, C, D, E), {A_, B_, C_, D_, E_}])
|
| 458 |
+
|
| 459 |
+
assert grid.width == 6
|
| 460 |
+
assert grid.height == 3
|
| 461 |
+
assert grid[0, 0] == A
|
| 462 |
+
assert grid[0, 1] == B
|
| 463 |
+
assert grid[0, 2] is None
|
| 464 |
+
assert grid[0, 3] == A_
|
| 465 |
+
assert grid[0, 4] == B_
|
| 466 |
+
assert grid[0, 5] is None
|
| 467 |
+
assert grid[1, 0] is None
|
| 468 |
+
assert grid[1, 1] == C
|
| 469 |
+
assert grid[1, 2] == D
|
| 470 |
+
assert grid[1, 3] is None
|
| 471 |
+
assert grid[1, 4] == C_
|
| 472 |
+
assert grid[1, 5] == D_
|
| 473 |
+
assert grid[2, 0] is None
|
| 474 |
+
assert grid[2, 1] is None
|
| 475 |
+
assert grid[2, 2] == E
|
| 476 |
+
assert grid[2, 3] is None
|
| 477 |
+
assert grid[2, 4] is None
|
| 478 |
+
assert grid[2, 5] == E_
|
| 479 |
+
assert grid.morphisms == morphisms
|
| 480 |
+
|
| 481 |
+
# Test the five lemma with object grouping and hints.
|
| 482 |
+
grid = DiagramGrid(d, {
|
| 483 |
+
FiniteSet(A, B, C, D, E): {"layout": "sequential",
|
| 484 |
+
"transpose": True},
|
| 485 |
+
FiniteSet(A_, B_, C_, D_, E_): {"layout": "sequential",
|
| 486 |
+
"transpose": True}},
|
| 487 |
+
transpose=True)
|
| 488 |
+
|
| 489 |
+
assert grid.width == 5
|
| 490 |
+
assert grid.height == 2
|
| 491 |
+
assert grid[0, 0] == A
|
| 492 |
+
assert grid[0, 1] == B
|
| 493 |
+
assert grid[0, 2] == C
|
| 494 |
+
assert grid[0, 3] == D
|
| 495 |
+
assert grid[0, 4] == E
|
| 496 |
+
assert grid[1, 0] == A_
|
| 497 |
+
assert grid[1, 1] == B_
|
| 498 |
+
assert grid[1, 2] == C_
|
| 499 |
+
assert grid[1, 3] == D_
|
| 500 |
+
assert grid[1, 4] == E_
|
| 501 |
+
assert grid.morphisms == morphisms
|
| 502 |
+
|
| 503 |
+
# A two-triangle disconnected diagram.
|
| 504 |
+
f = NamedMorphism(A, B, "f")
|
| 505 |
+
g = NamedMorphism(B, C, "g")
|
| 506 |
+
f_ = NamedMorphism(A_, B_, "f")
|
| 507 |
+
g_ = NamedMorphism(B_, C_, "g")
|
| 508 |
+
d = Diagram([f, g, f_, g_], {g * f: "unique", g_ * f_: "unique"})
|
| 509 |
+
grid = DiagramGrid(d)
|
| 510 |
+
|
| 511 |
+
assert grid.width == 4
|
| 512 |
+
assert grid.height == 2
|
| 513 |
+
assert grid[0, 0] == A
|
| 514 |
+
assert grid[0, 1] == B
|
| 515 |
+
assert grid[0, 2] == A_
|
| 516 |
+
assert grid[0, 3] == B_
|
| 517 |
+
assert grid[1, 0] == C
|
| 518 |
+
assert grid[1, 1] is None
|
| 519 |
+
assert grid[1, 2] == C_
|
| 520 |
+
assert grid[1, 3] is None
|
| 521 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), f_: FiniteSet(),
|
| 522 |
+
g_: FiniteSet(), g * f: FiniteSet("unique"),
|
| 523 |
+
g_ * f_: FiniteSet("unique")}
|
| 524 |
+
|
| 525 |
+
# A two-morphism disconnected diagram.
|
| 526 |
+
f = NamedMorphism(A, B, "f")
|
| 527 |
+
g = NamedMorphism(C, D, "g")
|
| 528 |
+
d = Diagram([f, g])
|
| 529 |
+
grid = DiagramGrid(d)
|
| 530 |
+
|
| 531 |
+
assert grid.width == 4
|
| 532 |
+
assert grid.height == 1
|
| 533 |
+
assert grid[0, 0] == A
|
| 534 |
+
assert grid[0, 1] == B
|
| 535 |
+
assert grid[0, 2] == C
|
| 536 |
+
assert grid[0, 3] == D
|
| 537 |
+
assert grid.morphisms == {f: FiniteSet(), g: FiniteSet()}
|
| 538 |
+
|
| 539 |
+
# Test a one-object diagram.
|
| 540 |
+
f = NamedMorphism(A, A, "f")
|
| 541 |
+
d = Diagram([f])
|
| 542 |
+
grid = DiagramGrid(d)
|
| 543 |
+
|
| 544 |
+
assert grid.width == 1
|
| 545 |
+
assert grid.height == 1
|
| 546 |
+
assert grid[0, 0] == A
|
| 547 |
+
|
| 548 |
+
# Test a two-object disconnected diagram.
|
| 549 |
+
g = NamedMorphism(B, B, "g")
|
| 550 |
+
d = Diagram([f, g])
|
| 551 |
+
grid = DiagramGrid(d)
|
| 552 |
+
|
| 553 |
+
assert grid.width == 2
|
| 554 |
+
assert grid.height == 1
|
| 555 |
+
assert grid[0, 0] == A
|
| 556 |
+
assert grid[0, 1] == B
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
def test_DiagramGrid_pseudopod():
|
| 560 |
+
# Test a diagram in which even growing a pseudopod does not
|
| 561 |
+
# eventually help.
|
| 562 |
+
A = Object("A")
|
| 563 |
+
B = Object("B")
|
| 564 |
+
C = Object("C")
|
| 565 |
+
D = Object("D")
|
| 566 |
+
E = Object("E")
|
| 567 |
+
F = Object("F")
|
| 568 |
+
A_ = Object("A'")
|
| 569 |
+
B_ = Object("B'")
|
| 570 |
+
C_ = Object("C'")
|
| 571 |
+
D_ = Object("D'")
|
| 572 |
+
E_ = Object("E'")
|
| 573 |
+
|
| 574 |
+
f1 = NamedMorphism(A, B, "f1")
|
| 575 |
+
f2 = NamedMorphism(A, C, "f2")
|
| 576 |
+
f3 = NamedMorphism(A, D, "f3")
|
| 577 |
+
f4 = NamedMorphism(A, E, "f4")
|
| 578 |
+
f5 = NamedMorphism(A, A_, "f5")
|
| 579 |
+
f6 = NamedMorphism(A, B_, "f6")
|
| 580 |
+
f7 = NamedMorphism(A, C_, "f7")
|
| 581 |
+
f8 = NamedMorphism(A, D_, "f8")
|
| 582 |
+
f9 = NamedMorphism(A, E_, "f9")
|
| 583 |
+
f10 = NamedMorphism(A, F, "f10")
|
| 584 |
+
d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10])
|
| 585 |
+
grid = DiagramGrid(d)
|
| 586 |
+
|
| 587 |
+
assert grid.width == 5
|
| 588 |
+
assert grid.height == 3
|
| 589 |
+
assert grid[0, 0] == E
|
| 590 |
+
assert grid[0, 1] == C
|
| 591 |
+
assert grid[0, 2] == C_
|
| 592 |
+
assert grid[0, 3] == E_
|
| 593 |
+
assert grid[0, 4] == F
|
| 594 |
+
assert grid[1, 0] == D
|
| 595 |
+
assert grid[1, 1] == A
|
| 596 |
+
assert grid[1, 2] == A_
|
| 597 |
+
assert grid[1, 3] is None
|
| 598 |
+
assert grid[1, 4] is None
|
| 599 |
+
assert grid[2, 0] == D_
|
| 600 |
+
assert grid[2, 1] == B
|
| 601 |
+
assert grid[2, 2] == B_
|
| 602 |
+
assert grid[2, 3] is None
|
| 603 |
+
assert grid[2, 4] is None
|
| 604 |
+
|
| 605 |
+
morphisms = {}
|
| 606 |
+
for f in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10]:
|
| 607 |
+
morphisms[f] = FiniteSet()
|
| 608 |
+
assert grid.morphisms == morphisms
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
def test_ArrowStringDescription():
|
| 612 |
+
astr = ArrowStringDescription("cm", "", None, "", "", "d", "r", "_", "f")
|
| 613 |
+
assert str(astr) == "\\ar[dr]_{f}"
|
| 614 |
+
|
| 615 |
+
astr = ArrowStringDescription("cm", "", 12, "", "", "d", "r", "_", "f")
|
| 616 |
+
assert str(astr) == "\\ar[dr]_{f}"
|
| 617 |
+
|
| 618 |
+
astr = ArrowStringDescription("cm", "^", 12, "", "", "d", "r", "_", "f")
|
| 619 |
+
assert str(astr) == "\\ar@/^12cm/[dr]_{f}"
|
| 620 |
+
|
| 621 |
+
astr = ArrowStringDescription("cm", "", 12, "r", "", "d", "r", "_", "f")
|
| 622 |
+
assert str(astr) == "\\ar[dr]_{f}"
|
| 623 |
+
|
| 624 |
+
astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f")
|
| 625 |
+
assert str(astr) == "\\ar@(r,u)[dr]_{f}"
|
| 626 |
+
|
| 627 |
+
astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f")
|
| 628 |
+
assert str(astr) == "\\ar@(r,u)[dr]_{f}"
|
| 629 |
+
|
| 630 |
+
astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f")
|
| 631 |
+
astr.arrow_style = "{-->}"
|
| 632 |
+
assert str(astr) == "\\ar@(r,u)@{-->}[dr]_{f}"
|
| 633 |
+
|
| 634 |
+
astr = ArrowStringDescription("cm", "_", 12, "", "", "d", "r", "_", "f")
|
| 635 |
+
astr.arrow_style = "{-->}"
|
| 636 |
+
assert str(astr) == "\\ar@/_12cm/@{-->}[dr]_{f}"
|
| 637 |
+
|
| 638 |
+
|
| 639 |
+
def test_XypicDiagramDrawer_line():
|
| 640 |
+
# A linear diagram.
|
| 641 |
+
A = Object("A")
|
| 642 |
+
B = Object("B")
|
| 643 |
+
C = Object("C")
|
| 644 |
+
D = Object("D")
|
| 645 |
+
E = Object("E")
|
| 646 |
+
|
| 647 |
+
f = NamedMorphism(A, B, "f")
|
| 648 |
+
g = NamedMorphism(B, C, "g")
|
| 649 |
+
h = NamedMorphism(C, D, "h")
|
| 650 |
+
i = NamedMorphism(D, E, "i")
|
| 651 |
+
d = Diagram([f, g, h, i])
|
| 652 |
+
grid = DiagramGrid(d, layout="sequential")
|
| 653 |
+
drawer = XypicDiagramDrawer()
|
| 654 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 655 |
+
"A \\ar[r]^{f} & B \\ar[r]^{g} & C \\ar[r]^{h} & D \\ar[r]^{i} & E \n" \
|
| 656 |
+
"}\n"
|
| 657 |
+
|
| 658 |
+
# The same diagram, transposed.
|
| 659 |
+
grid = DiagramGrid(d, layout="sequential", transpose=True)
|
| 660 |
+
drawer = XypicDiagramDrawer()
|
| 661 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 662 |
+
"A \\ar[d]^{f} \\\\\n" \
|
| 663 |
+
"B \\ar[d]^{g} \\\\\n" \
|
| 664 |
+
"C \\ar[d]^{h} \\\\\n" \
|
| 665 |
+
"D \\ar[d]^{i} \\\\\n" \
|
| 666 |
+
"E \n" \
|
| 667 |
+
"}\n"
|
| 668 |
+
|
| 669 |
+
|
| 670 |
+
def test_XypicDiagramDrawer_triangle():
|
| 671 |
+
# A triangle diagram.
|
| 672 |
+
A = Object("A")
|
| 673 |
+
B = Object("B")
|
| 674 |
+
C = Object("C")
|
| 675 |
+
f = NamedMorphism(A, B, "f")
|
| 676 |
+
g = NamedMorphism(B, C, "g")
|
| 677 |
+
|
| 678 |
+
d = Diagram([f, g], {g * f: "unique"})
|
| 679 |
+
grid = DiagramGrid(d)
|
| 680 |
+
drawer = XypicDiagramDrawer()
|
| 681 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 682 |
+
"A \\ar[d]_{g\\circ f} \\ar[r]^{f} & B \\ar[ld]^{g} \\\\\n" \
|
| 683 |
+
"C & \n" \
|
| 684 |
+
"}\n"
|
| 685 |
+
|
| 686 |
+
# The same diagram, transposed.
|
| 687 |
+
grid = DiagramGrid(d, transpose=True)
|
| 688 |
+
drawer = XypicDiagramDrawer()
|
| 689 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 690 |
+
"A \\ar[r]^{g\\circ f} \\ar[d]_{f} & C \\\\\n" \
|
| 691 |
+
"B \\ar[ru]_{g} & \n" \
|
| 692 |
+
"}\n"
|
| 693 |
+
|
| 694 |
+
# The same diagram, with a masked morphism.
|
| 695 |
+
assert drawer.draw(d, grid, masked=[g]) == "\\xymatrix{\n" \
|
| 696 |
+
"A \\ar[r]^{g\\circ f} \\ar[d]_{f} & C \\\\\n" \
|
| 697 |
+
"B & \n" \
|
| 698 |
+
"}\n"
|
| 699 |
+
|
| 700 |
+
# The same diagram with a formatter for "unique".
|
| 701 |
+
def formatter(astr):
|
| 702 |
+
astr.label = "\\exists !" + astr.label
|
| 703 |
+
astr.arrow_style = "{-->}"
|
| 704 |
+
|
| 705 |
+
drawer.arrow_formatters["unique"] = formatter
|
| 706 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 707 |
+
"A \\ar@{-->}[r]^{\\exists !g\\circ f} \\ar[d]_{f} & C \\\\\n" \
|
| 708 |
+
"B \\ar[ru]_{g} & \n" \
|
| 709 |
+
"}\n"
|
| 710 |
+
|
| 711 |
+
# The same diagram with a default formatter.
|
| 712 |
+
def default_formatter(astr):
|
| 713 |
+
astr.label_displacement = "(0.45)"
|
| 714 |
+
|
| 715 |
+
drawer.default_arrow_formatter = default_formatter
|
| 716 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 717 |
+
"A \\ar@{-->}[r]^(0.45){\\exists !g\\circ f} \\ar[d]_(0.45){f} & C \\\\\n" \
|
| 718 |
+
"B \\ar[ru]_(0.45){g} & \n" \
|
| 719 |
+
"}\n"
|
| 720 |
+
|
| 721 |
+
# A triangle diagram with a lot of morphisms between the same
|
| 722 |
+
# objects.
|
| 723 |
+
f1 = NamedMorphism(B, A, "f1")
|
| 724 |
+
f2 = NamedMorphism(A, B, "f2")
|
| 725 |
+
g1 = NamedMorphism(C, B, "g1")
|
| 726 |
+
g2 = NamedMorphism(B, C, "g2")
|
| 727 |
+
d = Diagram([f, f1, f2, g, g1, g2], {f1 * g1: "unique", g2 * f2: "unique"})
|
| 728 |
+
|
| 729 |
+
grid = DiagramGrid(d, transpose=True)
|
| 730 |
+
drawer = XypicDiagramDrawer()
|
| 731 |
+
assert drawer.draw(d, grid, masked=[f1*g1*g2*f2, g2*f2*f1*g1]) == \
|
| 732 |
+
"\\xymatrix{\n" \
|
| 733 |
+
"A \\ar[r]^{g_{2}\\circ f_{2}} \\ar[d]_{f} \\ar@/^3mm/[d]^{f_{2}} " \
|
| 734 |
+
"& C \\ar@/^3mm/[l]^{f_{1}\\circ g_{1}} \\ar@/^3mm/[ld]^{g_{1}} \\\\\n" \
|
| 735 |
+
"B \\ar@/^3mm/[u]^{f_{1}} \\ar[ru]_{g} \\ar@/^3mm/[ru]^{g_{2}} & \n" \
|
| 736 |
+
"}\n"
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
def test_XypicDiagramDrawer_cube():
|
| 740 |
+
# A cube diagram.
|
| 741 |
+
A1 = Object("A1")
|
| 742 |
+
A2 = Object("A2")
|
| 743 |
+
A3 = Object("A3")
|
| 744 |
+
A4 = Object("A4")
|
| 745 |
+
A5 = Object("A5")
|
| 746 |
+
A6 = Object("A6")
|
| 747 |
+
A7 = Object("A7")
|
| 748 |
+
A8 = Object("A8")
|
| 749 |
+
|
| 750 |
+
# The top face of the cube.
|
| 751 |
+
f1 = NamedMorphism(A1, A2, "f1")
|
| 752 |
+
f2 = NamedMorphism(A1, A3, "f2")
|
| 753 |
+
f3 = NamedMorphism(A2, A4, "f3")
|
| 754 |
+
f4 = NamedMorphism(A3, A4, "f3")
|
| 755 |
+
|
| 756 |
+
# The bottom face of the cube.
|
| 757 |
+
f5 = NamedMorphism(A5, A6, "f5")
|
| 758 |
+
f6 = NamedMorphism(A5, A7, "f6")
|
| 759 |
+
f7 = NamedMorphism(A6, A8, "f7")
|
| 760 |
+
f8 = NamedMorphism(A7, A8, "f8")
|
| 761 |
+
|
| 762 |
+
# The remaining morphisms.
|
| 763 |
+
f9 = NamedMorphism(A1, A5, "f9")
|
| 764 |
+
f10 = NamedMorphism(A2, A6, "f10")
|
| 765 |
+
f11 = NamedMorphism(A3, A7, "f11")
|
| 766 |
+
f12 = NamedMorphism(A4, A8, "f11")
|
| 767 |
+
|
| 768 |
+
d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12])
|
| 769 |
+
grid = DiagramGrid(d)
|
| 770 |
+
drawer = XypicDiagramDrawer()
|
| 771 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 772 |
+
"& A_{5} \\ar[r]^{f_{5}} \\ar[ldd]_{f_{6}} & A_{6} \\ar[rdd]^{f_{7}} " \
|
| 773 |
+
"& \\\\\n" \
|
| 774 |
+
"& A_{1} \\ar[r]^{f_{1}} \\ar[d]^{f_{2}} \\ar[u]^{f_{9}} & A_{2} " \
|
| 775 |
+
"\\ar[d]^{f_{3}} \\ar[u]_{f_{10}} & \\\\\n" \
|
| 776 |
+
"A_{7} \\ar@/_3mm/[rrr]_{f_{8}} & A_{3} \\ar[r]^{f_{3}} \\ar[l]_{f_{11}} " \
|
| 777 |
+
"& A_{4} \\ar[r]^{f_{11}} & A_{8} \n" \
|
| 778 |
+
"}\n"
|
| 779 |
+
|
| 780 |
+
# The same diagram, transposed.
|
| 781 |
+
grid = DiagramGrid(d, transpose=True)
|
| 782 |
+
drawer = XypicDiagramDrawer()
|
| 783 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 784 |
+
"& & A_{7} \\ar@/^3mm/[ddd]^{f_{8}} \\\\\n" \
|
| 785 |
+
"A_{5} \\ar[d]_{f_{5}} \\ar[rru]^{f_{6}} & A_{1} \\ar[d]^{f_{1}} " \
|
| 786 |
+
"\\ar[r]^{f_{2}} \\ar[l]^{f_{9}} & A_{3} \\ar[d]_{f_{3}} " \
|
| 787 |
+
"\\ar[u]^{f_{11}} \\\\\n" \
|
| 788 |
+
"A_{6} \\ar[rrd]_{f_{7}} & A_{2} \\ar[r]^{f_{3}} \\ar[l]^{f_{10}} " \
|
| 789 |
+
"& A_{4} \\ar[d]_{f_{11}} \\\\\n" \
|
| 790 |
+
"& & A_{8} \n" \
|
| 791 |
+
"}\n"
|
| 792 |
+
|
| 793 |
+
|
| 794 |
+
def test_XypicDiagramDrawer_curved_and_loops():
|
| 795 |
+
# A simple diagram, with a curved arrow.
|
| 796 |
+
A = Object("A")
|
| 797 |
+
B = Object("B")
|
| 798 |
+
C = Object("C")
|
| 799 |
+
D = Object("D")
|
| 800 |
+
|
| 801 |
+
f = NamedMorphism(A, B, "f")
|
| 802 |
+
g = NamedMorphism(B, C, "g")
|
| 803 |
+
h = NamedMorphism(D, A, "h")
|
| 804 |
+
k = NamedMorphism(D, B, "k")
|
| 805 |
+
d = Diagram([f, g, h, k])
|
| 806 |
+
grid = DiagramGrid(d)
|
| 807 |
+
drawer = XypicDiagramDrawer()
|
| 808 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 809 |
+
"A \\ar[r]_{f} & B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_3mm/[ll]_{h} \\\\\n" \
|
| 810 |
+
"& C & \n" \
|
| 811 |
+
"}\n"
|
| 812 |
+
|
| 813 |
+
# The same diagram, transposed.
|
| 814 |
+
grid = DiagramGrid(d, transpose=True)
|
| 815 |
+
drawer = XypicDiagramDrawer()
|
| 816 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 817 |
+
"A \\ar[d]^{f} & \\\\\n" \
|
| 818 |
+
"B \\ar[r]^{g} & C \\\\\n" \
|
| 819 |
+
"D \\ar[u]_{k} \\ar@/^3mm/[uu]^{h} & \n" \
|
| 820 |
+
"}\n"
|
| 821 |
+
|
| 822 |
+
# The same diagram, larger and rotated.
|
| 823 |
+
assert drawer.draw(d, grid, diagram_format="@+1cm@dr") == \
|
| 824 |
+
"\\xymatrix@+1cm@dr{\n" \
|
| 825 |
+
"A \\ar[d]^{f} & \\\\\n" \
|
| 826 |
+
"B \\ar[r]^{g} & C \\\\\n" \
|
| 827 |
+
"D \\ar[u]_{k} \\ar@/^3mm/[uu]^{h} & \n" \
|
| 828 |
+
"}\n"
|
| 829 |
+
|
| 830 |
+
# A simple diagram with three curved arrows.
|
| 831 |
+
h1 = NamedMorphism(D, A, "h1")
|
| 832 |
+
h2 = NamedMorphism(A, D, "h2")
|
| 833 |
+
k = NamedMorphism(D, B, "k")
|
| 834 |
+
d = Diagram([f, g, h, k, h1, h2])
|
| 835 |
+
grid = DiagramGrid(d)
|
| 836 |
+
drawer = XypicDiagramDrawer()
|
| 837 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 838 |
+
"A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} & B \\ar[d]^{g} & D \\ar[l]^{k} " \
|
| 839 |
+
"\\ar@/_7mm/[ll]_{h} \\ar@/_11mm/[ll]_{h_{1}} \\\\\n" \
|
| 840 |
+
"& C & \n" \
|
| 841 |
+
"}\n"
|
| 842 |
+
|
| 843 |
+
# The same diagram, transposed.
|
| 844 |
+
grid = DiagramGrid(d, transpose=True)
|
| 845 |
+
drawer = XypicDiagramDrawer()
|
| 846 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 847 |
+
"A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} & \\\\\n" \
|
| 848 |
+
"B \\ar[r]^{g} & C \\\\\n" \
|
| 849 |
+
"D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} & \n" \
|
| 850 |
+
"}\n"
|
| 851 |
+
|
| 852 |
+
# The same diagram, with "loop" morphisms.
|
| 853 |
+
l_A = NamedMorphism(A, A, "l_A")
|
| 854 |
+
l_D = NamedMorphism(D, D, "l_D")
|
| 855 |
+
l_C = NamedMorphism(C, C, "l_C")
|
| 856 |
+
d = Diagram([f, g, h, k, h1, h2, l_A, l_D, l_C])
|
| 857 |
+
grid = DiagramGrid(d)
|
| 858 |
+
drawer = XypicDiagramDrawer()
|
| 859 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 860 |
+
"A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} \\ar@(u,l)[]^{l_{A}} " \
|
| 861 |
+
"& B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_7mm/[ll]_{h} " \
|
| 862 |
+
"\\ar@/_11mm/[ll]_{h_{1}} \\ar@(r,u)[]^{l_{D}} \\\\\n" \
|
| 863 |
+
"& C \\ar@(l,d)[]^{l_{C}} & \n" \
|
| 864 |
+
"}\n"
|
| 865 |
+
|
| 866 |
+
# The same diagram with "loop" morphisms, transposed.
|
| 867 |
+
grid = DiagramGrid(d, transpose=True)
|
| 868 |
+
drawer = XypicDiagramDrawer()
|
| 869 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 870 |
+
"A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} \\ar@(r,u)[]^{l_{A}} & \\\\\n" \
|
| 871 |
+
"B \\ar[r]^{g} & C \\ar@(r,u)[]^{l_{C}} \\\\\n" \
|
| 872 |
+
"D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} " \
|
| 873 |
+
"\\ar@(l,d)[]^{l_{D}} & \n" \
|
| 874 |
+
"}\n"
|
| 875 |
+
|
| 876 |
+
# The same diagram with two "loop" morphisms per object.
|
| 877 |
+
l_A_ = NamedMorphism(A, A, "n_A")
|
| 878 |
+
l_D_ = NamedMorphism(D, D, "n_D")
|
| 879 |
+
l_C_ = NamedMorphism(C, C, "n_C")
|
| 880 |
+
d = Diagram([f, g, h, k, h1, h2, l_A, l_D, l_C, l_A_, l_D_, l_C_])
|
| 881 |
+
grid = DiagramGrid(d)
|
| 882 |
+
drawer = XypicDiagramDrawer()
|
| 883 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 884 |
+
"A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} \\ar@(u,l)[]^{l_{A}} " \
|
| 885 |
+
"\\ar@/^3mm/@(l,d)[]^{n_{A}} & B \\ar[d]^{g} & D \\ar[l]^{k} " \
|
| 886 |
+
"\\ar@/_7mm/[ll]_{h} \\ar@/_11mm/[ll]_{h_{1}} \\ar@(r,u)[]^{l_{D}} " \
|
| 887 |
+
"\\ar@/^3mm/@(d,r)[]^{n_{D}} \\\\\n" \
|
| 888 |
+
"& C \\ar@(l,d)[]^{l_{C}} \\ar@/^3mm/@(d,r)[]^{n_{C}} & \n" \
|
| 889 |
+
"}\n"
|
| 890 |
+
|
| 891 |
+
# The same diagram with two "loop" morphisms per object, transposed.
|
| 892 |
+
grid = DiagramGrid(d, transpose=True)
|
| 893 |
+
drawer = XypicDiagramDrawer()
|
| 894 |
+
assert drawer.draw(d, grid) == "\\xymatrix{\n" \
|
| 895 |
+
"A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} \\ar@(r,u)[]^{l_{A}} " \
|
| 896 |
+
"\\ar@/^3mm/@(u,l)[]^{n_{A}} & \\\\\n" \
|
| 897 |
+
"B \\ar[r]^{g} & C \\ar@(r,u)[]^{l_{C}} \\ar@/^3mm/@(d,r)[]^{n_{C}} \\\\\n" \
|
| 898 |
+
"D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} " \
|
| 899 |
+
"\\ar@(l,d)[]^{l_{D}} \\ar@/^3mm/@(d,r)[]^{n_{D}} & \n" \
|
| 900 |
+
"}\n"
|
| 901 |
+
|
| 902 |
+
|
| 903 |
+
def test_xypic_draw_diagram():
|
| 904 |
+
# A linear diagram.
|
| 905 |
+
A = Object("A")
|
| 906 |
+
B = Object("B")
|
| 907 |
+
C = Object("C")
|
| 908 |
+
D = Object("D")
|
| 909 |
+
E = Object("E")
|
| 910 |
+
|
| 911 |
+
f = NamedMorphism(A, B, "f")
|
| 912 |
+
g = NamedMorphism(B, C, "g")
|
| 913 |
+
h = NamedMorphism(C, D, "h")
|
| 914 |
+
i = NamedMorphism(D, E, "i")
|
| 915 |
+
d = Diagram([f, g, h, i])
|
| 916 |
+
|
| 917 |
+
grid = DiagramGrid(d, layout="sequential")
|
| 918 |
+
drawer = XypicDiagramDrawer()
|
| 919 |
+
assert drawer.draw(d, grid) == xypic_draw_diagram(d, layout="sequential")
|
lib/python3.12/site-packages/sympy/liealgebras/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 2 |
+
|
| 3 |
+
__all__ = ['CartanType']
|
lib/python3.12/site-packages/sympy/liealgebras/cartan_matrix.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import CartanType
|
| 2 |
+
|
| 3 |
+
def CartanMatrix(ct):
|
| 4 |
+
"""Access the Cartan matrix of a specific Lie algebra
|
| 5 |
+
|
| 6 |
+
Examples
|
| 7 |
+
========
|
| 8 |
+
|
| 9 |
+
>>> from sympy.liealgebras.cartan_matrix import CartanMatrix
|
| 10 |
+
>>> CartanMatrix("A2")
|
| 11 |
+
Matrix([
|
| 12 |
+
[ 2, -1],
|
| 13 |
+
[-1, 2]])
|
| 14 |
+
|
| 15 |
+
>>> CartanMatrix(['C', 3])
|
| 16 |
+
Matrix([
|
| 17 |
+
[ 2, -1, 0],
|
| 18 |
+
[-1, 2, -1],
|
| 19 |
+
[ 0, -2, 2]])
|
| 20 |
+
|
| 21 |
+
This method works by returning the Cartan matrix
|
| 22 |
+
which corresponds to Cartan type t.
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
return CartanType(ct).cartan_matrix()
|
lib/python3.12/site-packages/sympy/liealgebras/cartan_type.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import Atom, Basic
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class CartanType_generator():
|
| 5 |
+
"""
|
| 6 |
+
Constructor for actually creating things
|
| 7 |
+
"""
|
| 8 |
+
def __call__(self, *args):
|
| 9 |
+
c = args[0]
|
| 10 |
+
if isinstance(c, list):
|
| 11 |
+
letter, n = c[0], int(c[1])
|
| 12 |
+
elif isinstance(c, str):
|
| 13 |
+
letter, n = c[0], int(c[1:])
|
| 14 |
+
else:
|
| 15 |
+
raise TypeError("Argument must be a string (e.g. 'A3') or a list (e.g. ['A', 3])")
|
| 16 |
+
|
| 17 |
+
if n < 0:
|
| 18 |
+
raise ValueError("Lie algebra rank cannot be negative")
|
| 19 |
+
if letter == "A":
|
| 20 |
+
from . import type_a
|
| 21 |
+
return type_a.TypeA(n)
|
| 22 |
+
if letter == "B":
|
| 23 |
+
from . import type_b
|
| 24 |
+
return type_b.TypeB(n)
|
| 25 |
+
|
| 26 |
+
if letter == "C":
|
| 27 |
+
from . import type_c
|
| 28 |
+
return type_c.TypeC(n)
|
| 29 |
+
|
| 30 |
+
if letter == "D":
|
| 31 |
+
from . import type_d
|
| 32 |
+
return type_d.TypeD(n)
|
| 33 |
+
|
| 34 |
+
if letter == "E":
|
| 35 |
+
if n >= 6 and n <= 8:
|
| 36 |
+
from . import type_e
|
| 37 |
+
return type_e.TypeE(n)
|
| 38 |
+
|
| 39 |
+
if letter == "F":
|
| 40 |
+
if n == 4:
|
| 41 |
+
from . import type_f
|
| 42 |
+
return type_f.TypeF(n)
|
| 43 |
+
|
| 44 |
+
if letter == "G":
|
| 45 |
+
if n == 2:
|
| 46 |
+
from . import type_g
|
| 47 |
+
return type_g.TypeG(n)
|
| 48 |
+
|
| 49 |
+
CartanType = CartanType_generator()
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class Standard_Cartan(Atom):
|
| 53 |
+
"""
|
| 54 |
+
Concrete base class for Cartan types such as A4, etc
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
def __new__(cls, series, n):
|
| 58 |
+
obj = Basic.__new__(cls)
|
| 59 |
+
obj.n = n
|
| 60 |
+
obj.series = series
|
| 61 |
+
return obj
|
| 62 |
+
|
| 63 |
+
def rank(self):
|
| 64 |
+
"""
|
| 65 |
+
Returns the rank of the Lie algebra
|
| 66 |
+
"""
|
| 67 |
+
return self.n
|
| 68 |
+
|
| 69 |
+
def series(self):
|
| 70 |
+
"""
|
| 71 |
+
Returns the type of the Lie algebra
|
| 72 |
+
"""
|
| 73 |
+
return self.series
|
lib/python3.12/site-packages/sympy/liealgebras/dynkin_diagram.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import CartanType
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def DynkinDiagram(t):
|
| 5 |
+
"""Display the Dynkin diagram of a given Lie algebra
|
| 6 |
+
|
| 7 |
+
Works by generating the CartanType for the input, t, and then returning the
|
| 8 |
+
Dynkin diagram method from the individual classes.
|
| 9 |
+
|
| 10 |
+
Examples
|
| 11 |
+
========
|
| 12 |
+
|
| 13 |
+
>>> from sympy.liealgebras.dynkin_diagram import DynkinDiagram
|
| 14 |
+
>>> print(DynkinDiagram("A3"))
|
| 15 |
+
0---0---0
|
| 16 |
+
1 2 3
|
| 17 |
+
|
| 18 |
+
>>> print(DynkinDiagram("B4"))
|
| 19 |
+
0---0---0=>=0
|
| 20 |
+
1 2 3 4
|
| 21 |
+
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
return CartanType(t).dynkin_diagram()
|
lib/python3.12/site-packages/sympy/liealgebras/root_system.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import CartanType
|
| 2 |
+
from sympy.core.basic import Atom
|
| 3 |
+
|
| 4 |
+
class RootSystem(Atom):
|
| 5 |
+
"""Represent the root system of a simple Lie algebra
|
| 6 |
+
|
| 7 |
+
Every simple Lie algebra has a unique root system. To find the root
|
| 8 |
+
system, we first consider the Cartan subalgebra of g, which is the maximal
|
| 9 |
+
abelian subalgebra, and consider the adjoint action of g on this
|
| 10 |
+
subalgebra. There is a root system associated with this action. Now, a
|
| 11 |
+
root system over a vector space V is a set of finite vectors Phi (called
|
| 12 |
+
roots), which satisfy:
|
| 13 |
+
|
| 14 |
+
1. The roots span V
|
| 15 |
+
2. The only scalar multiples of x in Phi are x and -x
|
| 16 |
+
3. For every x in Phi, the set Phi is closed under reflection
|
| 17 |
+
through the hyperplane perpendicular to x.
|
| 18 |
+
4. If x and y are roots in Phi, then the projection of y onto
|
| 19 |
+
the line through x is a half-integral multiple of x.
|
| 20 |
+
|
| 21 |
+
Now, there is a subset of Phi, which we will call Delta, such that:
|
| 22 |
+
1. Delta is a basis of V
|
| 23 |
+
2. Each root x in Phi can be written x = sum k_y y for y in Delta
|
| 24 |
+
|
| 25 |
+
The elements of Delta are called the simple roots.
|
| 26 |
+
Therefore, we see that the simple roots span the root space of a given
|
| 27 |
+
simple Lie algebra.
|
| 28 |
+
|
| 29 |
+
References
|
| 30 |
+
==========
|
| 31 |
+
|
| 32 |
+
.. [1] https://en.wikipedia.org/wiki/Root_system
|
| 33 |
+
.. [2] Lie Algebras and Representation Theory - Humphreys
|
| 34 |
+
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __new__(cls, cartantype):
|
| 38 |
+
"""Create a new RootSystem object
|
| 39 |
+
|
| 40 |
+
This method assigns an attribute called cartan_type to each instance of
|
| 41 |
+
a RootSystem object. When an instance of RootSystem is called, it
|
| 42 |
+
needs an argument, which should be an instance of a simple Lie algebra.
|
| 43 |
+
We then take the CartanType of this argument and set it as the
|
| 44 |
+
cartan_type attribute of the RootSystem instance.
|
| 45 |
+
|
| 46 |
+
"""
|
| 47 |
+
obj = Atom.__new__(cls)
|
| 48 |
+
obj.cartan_type = CartanType(cartantype)
|
| 49 |
+
return obj
|
| 50 |
+
|
| 51 |
+
def simple_roots(self):
|
| 52 |
+
"""Generate the simple roots of the Lie algebra
|
| 53 |
+
|
| 54 |
+
The rank of the Lie algebra determines the number of simple roots that
|
| 55 |
+
it has. This method obtains the rank of the Lie algebra, and then uses
|
| 56 |
+
the simple_root method from the Lie algebra classes to generate all the
|
| 57 |
+
simple roots.
|
| 58 |
+
|
| 59 |
+
Examples
|
| 60 |
+
========
|
| 61 |
+
|
| 62 |
+
>>> from sympy.liealgebras.root_system import RootSystem
|
| 63 |
+
>>> c = RootSystem("A3")
|
| 64 |
+
>>> roots = c.simple_roots()
|
| 65 |
+
>>> roots
|
| 66 |
+
{1: [1, -1, 0, 0], 2: [0, 1, -1, 0], 3: [0, 0, 1, -1]}
|
| 67 |
+
|
| 68 |
+
"""
|
| 69 |
+
n = self.cartan_type.rank()
|
| 70 |
+
roots = {}
|
| 71 |
+
for i in range(1, n+1):
|
| 72 |
+
root = self.cartan_type.simple_root(i)
|
| 73 |
+
roots[i] = root
|
| 74 |
+
return roots
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def all_roots(self):
|
| 78 |
+
"""Generate all the roots of a given root system
|
| 79 |
+
|
| 80 |
+
The result is a dictionary where the keys are integer numbers. It
|
| 81 |
+
generates the roots by getting the dictionary of all positive roots
|
| 82 |
+
from the bases classes, and then taking each root, and multiplying it
|
| 83 |
+
by -1 and adding it to the dictionary. In this way all the negative
|
| 84 |
+
roots are generated.
|
| 85 |
+
|
| 86 |
+
"""
|
| 87 |
+
alpha = self.cartan_type.positive_roots()
|
| 88 |
+
keys = list(alpha.keys())
|
| 89 |
+
k = max(keys)
|
| 90 |
+
for val in keys:
|
| 91 |
+
k += 1
|
| 92 |
+
root = alpha[val]
|
| 93 |
+
newroot = [-x for x in root]
|
| 94 |
+
alpha[k] = newroot
|
| 95 |
+
return alpha
|
| 96 |
+
|
| 97 |
+
def root_space(self):
|
| 98 |
+
"""Return the span of the simple roots
|
| 99 |
+
|
| 100 |
+
The root space is the vector space spanned by the simple roots, i.e. it
|
| 101 |
+
is a vector space with a distinguished basis, the simple roots. This
|
| 102 |
+
method returns a string that represents the root space as the span of
|
| 103 |
+
the simple roots, alpha[1],...., alpha[n].
|
| 104 |
+
|
| 105 |
+
Examples
|
| 106 |
+
========
|
| 107 |
+
|
| 108 |
+
>>> from sympy.liealgebras.root_system import RootSystem
|
| 109 |
+
>>> c = RootSystem("A3")
|
| 110 |
+
>>> c.root_space()
|
| 111 |
+
'alpha[1] + alpha[2] + alpha[3]'
|
| 112 |
+
|
| 113 |
+
"""
|
| 114 |
+
n = self.cartan_type.rank()
|
| 115 |
+
rs = " + ".join("alpha["+str(i) +"]" for i in range(1, n+1))
|
| 116 |
+
return rs
|
| 117 |
+
|
| 118 |
+
def add_simple_roots(self, root1, root2):
|
| 119 |
+
"""Add two simple roots together
|
| 120 |
+
|
| 121 |
+
The function takes as input two integers, root1 and root2. It then
|
| 122 |
+
uses these integers as keys in the dictionary of simple roots, and gets
|
| 123 |
+
the corresponding simple roots, and then adds them together.
|
| 124 |
+
|
| 125 |
+
Examples
|
| 126 |
+
========
|
| 127 |
+
|
| 128 |
+
>>> from sympy.liealgebras.root_system import RootSystem
|
| 129 |
+
>>> c = RootSystem("A3")
|
| 130 |
+
>>> newroot = c.add_simple_roots(1, 2)
|
| 131 |
+
>>> newroot
|
| 132 |
+
[1, 0, -1, 0]
|
| 133 |
+
|
| 134 |
+
"""
|
| 135 |
+
|
| 136 |
+
alpha = self.simple_roots()
|
| 137 |
+
if root1 > len(alpha) or root2 > len(alpha):
|
| 138 |
+
raise ValueError("You've used a root that doesn't exist!")
|
| 139 |
+
a1 = alpha[root1]
|
| 140 |
+
a2 = alpha[root2]
|
| 141 |
+
newroot = [_a1 + _a2 for _a1, _a2 in zip(a1, a2)]
|
| 142 |
+
return newroot
|
| 143 |
+
|
| 144 |
+
def add_as_roots(self, root1, root2):
|
| 145 |
+
"""Add two roots together if and only if their sum is also a root
|
| 146 |
+
|
| 147 |
+
It takes as input two vectors which should be roots. It then computes
|
| 148 |
+
their sum and checks if it is in the list of all possible roots. If it
|
| 149 |
+
is, it returns the sum. Otherwise it returns a string saying that the
|
| 150 |
+
sum is not a root.
|
| 151 |
+
|
| 152 |
+
Examples
|
| 153 |
+
========
|
| 154 |
+
|
| 155 |
+
>>> from sympy.liealgebras.root_system import RootSystem
|
| 156 |
+
>>> c = RootSystem("A3")
|
| 157 |
+
>>> c.add_as_roots([1, 0, -1, 0], [0, 0, 1, -1])
|
| 158 |
+
[1, 0, 0, -1]
|
| 159 |
+
>>> c.add_as_roots([1, -1, 0, 0], [0, 0, -1, 1])
|
| 160 |
+
'The sum of these two roots is not a root'
|
| 161 |
+
|
| 162 |
+
"""
|
| 163 |
+
alpha = self.all_roots()
|
| 164 |
+
newroot = [r1 + r2 for r1, r2 in zip(root1, root2)]
|
| 165 |
+
if newroot in alpha.values():
|
| 166 |
+
return newroot
|
| 167 |
+
else:
|
| 168 |
+
return "The sum of these two roots is not a root"
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def cartan_matrix(self):
|
| 172 |
+
"""Cartan matrix of Lie algebra associated with this root system
|
| 173 |
+
|
| 174 |
+
Examples
|
| 175 |
+
========
|
| 176 |
+
|
| 177 |
+
>>> from sympy.liealgebras.root_system import RootSystem
|
| 178 |
+
>>> c = RootSystem("A3")
|
| 179 |
+
>>> c.cartan_matrix()
|
| 180 |
+
Matrix([
|
| 181 |
+
[ 2, -1, 0],
|
| 182 |
+
[-1, 2, -1],
|
| 183 |
+
[ 0, -1, 2]])
|
| 184 |
+
"""
|
| 185 |
+
return self.cartan_type.cartan_matrix()
|
| 186 |
+
|
| 187 |
+
def dynkin_diagram(self):
|
| 188 |
+
"""Dynkin diagram of the Lie algebra associated with this root system
|
| 189 |
+
|
| 190 |
+
Examples
|
| 191 |
+
========
|
| 192 |
+
|
| 193 |
+
>>> from sympy.liealgebras.root_system import RootSystem
|
| 194 |
+
>>> c = RootSystem("A3")
|
| 195 |
+
>>> print(c.dynkin_diagram())
|
| 196 |
+
0---0---0
|
| 197 |
+
1 2 3
|
| 198 |
+
"""
|
| 199 |
+
return self.cartan_type.dynkin_diagram()
|
lib/python3.12/site-packages/sympy/liealgebras/tests/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/sympy/liealgebras/tests/__pycache__/test_weyl_group.cpython-312.pyc
ADDED
|
Binary file (2.92 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_cartan_matrix.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_matrix import CartanMatrix
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
|
| 4 |
+
def test_CartanMatrix():
|
| 5 |
+
c = CartanMatrix("A3")
|
| 6 |
+
m = Matrix(3, 3, [2, -1, 0, -1, 2, -1, 0, -1, 2])
|
| 7 |
+
assert c == m
|
| 8 |
+
a = CartanMatrix(["G",2])
|
| 9 |
+
mt = Matrix(2, 2, [2, -1, -3, 2])
|
| 10 |
+
assert a == mt
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_cartan_type.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType, Standard_Cartan
|
| 2 |
+
|
| 3 |
+
def test_Standard_Cartan():
|
| 4 |
+
c = CartanType("A4")
|
| 5 |
+
assert c.rank() == 4
|
| 6 |
+
assert c.series == "A"
|
| 7 |
+
m = Standard_Cartan("A", 2)
|
| 8 |
+
assert m.rank() == 2
|
| 9 |
+
assert m.series == "A"
|
| 10 |
+
b = CartanType("B12")
|
| 11 |
+
assert b.rank() == 12
|
| 12 |
+
assert b.series == "B"
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_dynkin_diagram.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.dynkin_diagram import DynkinDiagram
|
| 2 |
+
|
| 3 |
+
def test_DynkinDiagram():
|
| 4 |
+
c = DynkinDiagram("A3")
|
| 5 |
+
diag = "0---0---0\n1 2 3"
|
| 6 |
+
assert c == diag
|
| 7 |
+
ct = DynkinDiagram(["B", 3])
|
| 8 |
+
diag2 = "0---0=>=0\n1 2 3"
|
| 9 |
+
assert ct == diag2
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_root_system.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.root_system import RootSystem
|
| 2 |
+
from sympy.liealgebras.type_a import TypeA
|
| 3 |
+
from sympy.matrices import Matrix
|
| 4 |
+
|
| 5 |
+
def test_root_system():
|
| 6 |
+
c = RootSystem("A3")
|
| 7 |
+
assert c.cartan_type == TypeA(3)
|
| 8 |
+
assert c.simple_roots() == {1: [1, -1, 0, 0], 2: [0, 1, -1, 0], 3: [0, 0, 1, -1]}
|
| 9 |
+
assert c.root_space() == "alpha[1] + alpha[2] + alpha[3]"
|
| 10 |
+
assert c.cartan_matrix() == Matrix([[ 2, -1, 0], [-1, 2, -1], [ 0, -1, 2]])
|
| 11 |
+
assert c.dynkin_diagram() == "0---0---0\n1 2 3"
|
| 12 |
+
assert c.add_simple_roots(1, 2) == [1, 0, -1, 0]
|
| 13 |
+
assert c.all_roots() == {1: [1, -1, 0, 0], 2: [1, 0, -1, 0],
|
| 14 |
+
3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1],
|
| 15 |
+
6: [0, 0, 1, -1], 7: [-1, 1, 0, 0], 8: [-1, 0, 1, 0],
|
| 16 |
+
9: [-1, 0, 0, 1], 10: [0, -1, 1, 0],
|
| 17 |
+
11: [0, -1, 0, 1], 12: [0, 0, -1, 1]}
|
| 18 |
+
assert c.add_as_roots([1, 0, -1, 0], [0, 0, 1, -1]) == [1, 0, 0, -1]
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_A.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
|
| 4 |
+
def test_type_A():
|
| 5 |
+
c = CartanType("A3")
|
| 6 |
+
m = Matrix(3, 3, [2, -1, 0, -1, 2, -1, 0, -1, 2])
|
| 7 |
+
assert m == c.cartan_matrix()
|
| 8 |
+
assert c.basis() == 8
|
| 9 |
+
assert c.roots() == 12
|
| 10 |
+
assert c.dimension() == 4
|
| 11 |
+
assert c.simple_root(1) == [1, -1, 0, 0]
|
| 12 |
+
assert c.highest_root() == [1, 0, 0, -1]
|
| 13 |
+
assert c.lie_algebra() == "su(4)"
|
| 14 |
+
diag = "0---0---0\n1 2 3"
|
| 15 |
+
assert c.dynkin_diagram() == diag
|
| 16 |
+
assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 0, -1, 0],
|
| 17 |
+
3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_B.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
|
| 4 |
+
def test_type_B():
|
| 5 |
+
c = CartanType("B3")
|
| 6 |
+
m = Matrix(3, 3, [2, -1, 0, -1, 2, -2, 0, -1, 2])
|
| 7 |
+
assert m == c.cartan_matrix()
|
| 8 |
+
assert c.dimension() == 3
|
| 9 |
+
assert c.roots() == 18
|
| 10 |
+
assert c.simple_root(3) == [0, 0, 1]
|
| 11 |
+
assert c.basis() == 3
|
| 12 |
+
assert c.lie_algebra() == "so(6)"
|
| 13 |
+
diag = "0---0=>=0\n1 2 3"
|
| 14 |
+
assert c.dynkin_diagram() == diag
|
| 15 |
+
assert c.positive_roots() == {1: [1, -1, 0], 2: [1, 1, 0], 3: [1, 0, -1],
|
| 16 |
+
4: [1, 0, 1], 5: [0, 1, -1], 6: [0, 1, 1], 7: [1, 0, 0],
|
| 17 |
+
8: [0, 1, 0], 9: [0, 0, 1]}
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_C.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
|
| 4 |
+
def test_type_C():
|
| 5 |
+
c = CartanType("C4")
|
| 6 |
+
m = Matrix(4, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -2, 2])
|
| 7 |
+
assert c.cartan_matrix() == m
|
| 8 |
+
assert c.dimension() == 4
|
| 9 |
+
assert c.simple_root(4) == [0, 0, 0, 2]
|
| 10 |
+
assert c.roots() == 32
|
| 11 |
+
assert c.basis() == 36
|
| 12 |
+
assert c.lie_algebra() == "sp(8)"
|
| 13 |
+
t = CartanType(['C', 3])
|
| 14 |
+
assert t.dimension() == 3
|
| 15 |
+
diag = "0---0---0=<=0\n1 2 3 4"
|
| 16 |
+
assert c.dynkin_diagram() == diag
|
| 17 |
+
assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 1, 0, 0],
|
| 18 |
+
3: [1, 0, -1, 0], 4: [1, 0, 1, 0], 5: [1, 0, 0, -1],
|
| 19 |
+
6: [1, 0, 0, 1], 7: [0, 1, -1, 0], 8: [0, 1, 1, 0],
|
| 20 |
+
9: [0, 1, 0, -1], 10: [0, 1, 0, 1], 11: [0, 0, 1, -1],
|
| 21 |
+
12: [0, 0, 1, 1], 13: [2, 0, 0, 0], 14: [0, 2, 0, 0], 15: [0, 0, 2, 0],
|
| 22 |
+
16: [0, 0, 0, 2]}
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_D.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_type_D():
|
| 7 |
+
c = CartanType("D4")
|
| 8 |
+
m = Matrix(4, 4, [2, -1, 0, 0, -1, 2, -1, -1, 0, -1, 2, 0, 0, -1, 0, 2])
|
| 9 |
+
assert c.cartan_matrix() == m
|
| 10 |
+
assert c.basis() == 6
|
| 11 |
+
assert c.lie_algebra() == "so(8)"
|
| 12 |
+
assert c.roots() == 24
|
| 13 |
+
assert c.simple_root(3) == [0, 0, 1, -1]
|
| 14 |
+
diag = " 3\n 0\n |\n |\n0---0---0\n1 2 4"
|
| 15 |
+
assert diag == c.dynkin_diagram()
|
| 16 |
+
assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 1, 0, 0],
|
| 17 |
+
3: [1, 0, -1, 0], 4: [1, 0, 1, 0], 5: [1, 0, 0, -1], 6: [1, 0, 0, 1],
|
| 18 |
+
7: [0, 1, -1, 0], 8: [0, 1, 1, 0], 9: [0, 1, 0, -1], 10: [0, 1, 0, 1],
|
| 19 |
+
11: [0, 0, 1, -1], 12: [0, 0, 1, 1]}
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_E.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
|
| 4 |
+
def test_type_E():
|
| 5 |
+
c = CartanType("E6")
|
| 6 |
+
m = Matrix(6, 6, [2, 0, -1, 0, 0, 0, 0, 2, 0, -1, 0, 0,
|
| 7 |
+
-1, 0, 2, -1, 0, 0, 0, -1, -1, 2, -1, 0, 0, 0, 0,
|
| 8 |
+
-1, 2, -1, 0, 0, 0, 0, -1, 2])
|
| 9 |
+
assert c.cartan_matrix() == m
|
| 10 |
+
assert c.dimension() == 8
|
| 11 |
+
assert c.simple_root(6) == [0, 0, 0, -1, 1, 0, 0, 0]
|
| 12 |
+
assert c.roots() == 72
|
| 13 |
+
assert c.basis() == 78
|
| 14 |
+
diag = " "*8 + "2\n" + " "*8 + "0\n" + " "*8 + "|\n" + " "*8 + "|\n"
|
| 15 |
+
diag += "---".join("0" for i in range(1, 6))+"\n"
|
| 16 |
+
diag += "1 " + " ".join(str(i) for i in range(3, 7))
|
| 17 |
+
assert c.dynkin_diagram() == diag
|
| 18 |
+
posroots = c.positive_roots()
|
| 19 |
+
assert posroots[8] == [1, 0, 0, 0, 1, 0, 0, 0]
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_F.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
from sympy.core.backend import S
|
| 4 |
+
|
| 5 |
+
def test_type_F():
|
| 6 |
+
c = CartanType("F4")
|
| 7 |
+
m = Matrix(4, 4, [2, -1, 0, 0, -1, 2, -2, 0, 0, -1, 2, -1, 0, 0, -1, 2])
|
| 8 |
+
assert c.cartan_matrix() == m
|
| 9 |
+
assert c.dimension() == 4
|
| 10 |
+
assert c.simple_root(1) == [1, -1, 0, 0]
|
| 11 |
+
assert c.simple_root(2) == [0, 1, -1, 0]
|
| 12 |
+
assert c.simple_root(3) == [0, 0, 0, 1]
|
| 13 |
+
assert c.simple_root(4) == [-S.Half, -S.Half, -S.Half, -S.Half]
|
| 14 |
+
assert c.roots() == 48
|
| 15 |
+
assert c.basis() == 52
|
| 16 |
+
diag = "0---0=>=0---0\n" + " ".join(str(i) for i in range(1, 5))
|
| 17 |
+
assert c.dynkin_diagram() == diag
|
| 18 |
+
assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 1, 0, 0], 3: [1, 0, -1, 0],
|
| 19 |
+
4: [1, 0, 1, 0], 5: [1, 0, 0, -1], 6: [1, 0, 0, 1], 7: [0, 1, -1, 0],
|
| 20 |
+
8: [0, 1, 1, 0], 9: [0, 1, 0, -1], 10: [0, 1, 0, 1], 11: [0, 0, 1, -1],
|
| 21 |
+
12: [0, 0, 1, 1], 13: [1, 0, 0, 0], 14: [0, 1, 0, 0], 15: [0, 0, 1, 0],
|
| 22 |
+
16: [0, 0, 0, 1], 17: [S.Half, S.Half, S.Half, S.Half], 18: [S.Half, -S.Half, S.Half, S.Half],
|
| 23 |
+
19: [S.Half, S.Half, -S.Half, S.Half], 20: [S.Half, S.Half, S.Half, -S.Half], 21: [S.Half, S.Half, -S.Half, -S.Half],
|
| 24 |
+
22: [S.Half, -S.Half, S.Half, -S.Half], 23: [S.Half, -S.Half, -S.Half, S.Half], 24: [S.Half, -S.Half, -S.Half, -S.Half]}
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_type_G.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
from sympy.liealgebras.cartan_type import CartanType
|
| 3 |
+
from sympy.matrices import Matrix
|
| 4 |
+
|
| 5 |
+
def test_type_G():
|
| 6 |
+
c = CartanType("G2")
|
| 7 |
+
m = Matrix(2, 2, [2, -1, -3, 2])
|
| 8 |
+
assert c.cartan_matrix() == m
|
| 9 |
+
assert c.simple_root(2) == [1, -2, 1]
|
| 10 |
+
assert c.basis() == 14
|
| 11 |
+
assert c.roots() == 12
|
| 12 |
+
assert c.dimension() == 3
|
| 13 |
+
diag = "0≡<≡0\n1 2"
|
| 14 |
+
assert diag == c.dynkin_diagram()
|
| 15 |
+
assert c.positive_roots() == {1: [0, 1, -1], 2: [1, -2, 1], 3: [1, -1, 0],
|
| 16 |
+
4: [1, 0, 1], 5: [1, 1, -2], 6: [2, -1, -1]}
|
lib/python3.12/site-packages/sympy/liealgebras/tests/test_weyl_group.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.weyl_group import WeylGroup
|
| 2 |
+
from sympy.matrices import Matrix
|
| 3 |
+
|
| 4 |
+
def test_weyl_group():
|
| 5 |
+
c = WeylGroup("A3")
|
| 6 |
+
assert c.matrix_form('r1*r2') == Matrix([[0, 0, 1, 0], [1, 0, 0, 0],
|
| 7 |
+
[0, 1, 0, 0], [0, 0, 0, 1]])
|
| 8 |
+
assert c.generators() == ['r1', 'r2', 'r3']
|
| 9 |
+
assert c.group_order() == 24.0
|
| 10 |
+
assert c.group_name() == "S4: the symmetric group acting on 4 elements."
|
| 11 |
+
assert c.coxeter_diagram() == "0---0---0\n1 2 3"
|
| 12 |
+
assert c.element_order('r1*r2*r3') == 4
|
| 13 |
+
assert c.element_order('r1*r3*r2*r3') == 3
|
| 14 |
+
d = WeylGroup("B5")
|
| 15 |
+
assert d.group_order() == 3840
|
| 16 |
+
assert d.element_order('r1*r2*r4*r5') == 12
|
| 17 |
+
assert d.matrix_form('r2*r3') == Matrix([[0, 0, 1, 0, 0], [1, 0, 0, 0, 0],
|
| 18 |
+
[0, 1, 0, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1]])
|
| 19 |
+
assert d.element_order('r1*r2*r1*r3*r5') == 6
|
| 20 |
+
e = WeylGroup("D5")
|
| 21 |
+
assert e.element_order('r2*r3*r5') == 4
|
| 22 |
+
assert e.matrix_form('r2*r3*r5') == Matrix([[1, 0, 0, 0, 0], [0, 0, 0, 0, -1],
|
| 23 |
+
[0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, -1, 0]])
|
| 24 |
+
f = WeylGroup("G2")
|
| 25 |
+
assert f.element_order('r1*r2*r1*r2') == 3
|
| 26 |
+
assert f.element_order('r2*r1*r1*r2') == 1
|
| 27 |
+
|
| 28 |
+
assert f.matrix_form('r1*r2*r1*r2') == Matrix([[0, 1, 0], [0, 0, 1], [1, 0, 0]])
|
| 29 |
+
g = WeylGroup("F4")
|
| 30 |
+
assert g.matrix_form('r2*r3') == Matrix([[1, 0, 0, 0], [0, 1, 0, 0],
|
| 31 |
+
[0, 0, 0, -1], [0, 0, 1, 0]])
|
| 32 |
+
|
| 33 |
+
assert g.element_order('r2*r3') == 4
|
| 34 |
+
h = WeylGroup("E6")
|
| 35 |
+
assert h.group_order() == 51840
|
lib/python3.12/site-packages/sympy/liealgebras/type_a.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.liealgebras.cartan_type import Standard_Cartan
|
| 2 |
+
from sympy.core.backend import eye
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class TypeA(Standard_Cartan):
|
| 6 |
+
"""
|
| 7 |
+
This class contains the information about
|
| 8 |
+
the A series of simple Lie algebras.
|
| 9 |
+
====
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
def __new__(cls, n):
|
| 13 |
+
if n < 1:
|
| 14 |
+
raise ValueError("n cannot be less than 1")
|
| 15 |
+
return Standard_Cartan.__new__(cls, "A", n)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def dimension(self):
|
| 19 |
+
"""Dimension of the vector space V underlying the Lie algebra
|
| 20 |
+
|
| 21 |
+
Examples
|
| 22 |
+
========
|
| 23 |
+
|
| 24 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 25 |
+
>>> c = CartanType("A4")
|
| 26 |
+
>>> c.dimension()
|
| 27 |
+
5
|
| 28 |
+
"""
|
| 29 |
+
return self.n+1
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def basic_root(self, i, j):
|
| 33 |
+
"""
|
| 34 |
+
This is a method just to generate roots
|
| 35 |
+
with a 1 iin the ith position and a -1
|
| 36 |
+
in the jth position.
|
| 37 |
+
|
| 38 |
+
"""
|
| 39 |
+
|
| 40 |
+
n = self.n
|
| 41 |
+
root = [0]*(n+1)
|
| 42 |
+
root[i] = 1
|
| 43 |
+
root[j] = -1
|
| 44 |
+
return root
|
| 45 |
+
|
| 46 |
+
def simple_root(self, i):
|
| 47 |
+
"""
|
| 48 |
+
Every lie algebra has a unique root system.
|
| 49 |
+
Given a root system Q, there is a subset of the
|
| 50 |
+
roots such that an element of Q is called a
|
| 51 |
+
simple root if it cannot be written as the sum
|
| 52 |
+
of two elements in Q. If we let D denote the
|
| 53 |
+
set of simple roots, then it is clear that every
|
| 54 |
+
element of Q can be written as a linear combination
|
| 55 |
+
of elements of D with all coefficients non-negative.
|
| 56 |
+
|
| 57 |
+
In A_n the ith simple root is the root which has a 1
|
| 58 |
+
in the ith position, a -1 in the (i+1)th position,
|
| 59 |
+
and zeroes elsewhere.
|
| 60 |
+
|
| 61 |
+
This method returns the ith simple root for the A series.
|
| 62 |
+
|
| 63 |
+
Examples
|
| 64 |
+
========
|
| 65 |
+
|
| 66 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 67 |
+
>>> c = CartanType("A4")
|
| 68 |
+
>>> c.simple_root(1)
|
| 69 |
+
[1, -1, 0, 0, 0]
|
| 70 |
+
|
| 71 |
+
"""
|
| 72 |
+
|
| 73 |
+
return self.basic_root(i-1, i)
|
| 74 |
+
|
| 75 |
+
def positive_roots(self):
|
| 76 |
+
"""
|
| 77 |
+
This method generates all the positive roots of
|
| 78 |
+
A_n. This is half of all of the roots of A_n;
|
| 79 |
+
by multiplying all the positive roots by -1 we
|
| 80 |
+
get the negative roots.
|
| 81 |
+
|
| 82 |
+
Examples
|
| 83 |
+
========
|
| 84 |
+
|
| 85 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 86 |
+
>>> c = CartanType("A3")
|
| 87 |
+
>>> c.positive_roots()
|
| 88 |
+
{1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0],
|
| 89 |
+
5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
n = self.n
|
| 93 |
+
posroots = {}
|
| 94 |
+
k = 0
|
| 95 |
+
for i in range(0, n):
|
| 96 |
+
for j in range(i+1, n+1):
|
| 97 |
+
k += 1
|
| 98 |
+
posroots[k] = self.basic_root(i, j)
|
| 99 |
+
return posroots
|
| 100 |
+
|
| 101 |
+
def highest_root(self):
|
| 102 |
+
"""
|
| 103 |
+
Returns the highest weight root for A_n
|
| 104 |
+
"""
|
| 105 |
+
|
| 106 |
+
return self.basic_root(0, self.n)
|
| 107 |
+
|
| 108 |
+
def roots(self):
|
| 109 |
+
"""
|
| 110 |
+
Returns the total number of roots for A_n
|
| 111 |
+
"""
|
| 112 |
+
n = self.n
|
| 113 |
+
return n*(n+1)
|
| 114 |
+
|
| 115 |
+
def cartan_matrix(self):
|
| 116 |
+
"""
|
| 117 |
+
Returns the Cartan matrix for A_n.
|
| 118 |
+
The Cartan matrix matrix for a Lie algebra is
|
| 119 |
+
generated by assigning an ordering to the simple
|
| 120 |
+
roots, (alpha[1], ...., alpha[l]). Then the ijth
|
| 121 |
+
entry of the Cartan matrix is (<alpha[i],alpha[j]>).
|
| 122 |
+
|
| 123 |
+
Examples
|
| 124 |
+
========
|
| 125 |
+
|
| 126 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 127 |
+
>>> c = CartanType('A4')
|
| 128 |
+
>>> c.cartan_matrix()
|
| 129 |
+
Matrix([
|
| 130 |
+
[ 2, -1, 0, 0],
|
| 131 |
+
[-1, 2, -1, 0],
|
| 132 |
+
[ 0, -1, 2, -1],
|
| 133 |
+
[ 0, 0, -1, 2]])
|
| 134 |
+
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
n = self.n
|
| 138 |
+
m = 2 * eye(n)
|
| 139 |
+
i = 1
|
| 140 |
+
while i < n-1:
|
| 141 |
+
m[i, i+1] = -1
|
| 142 |
+
m[i, i-1] = -1
|
| 143 |
+
i += 1
|
| 144 |
+
m[0,1] = -1
|
| 145 |
+
m[n-1, n-2] = -1
|
| 146 |
+
return m
|
| 147 |
+
|
| 148 |
+
def basis(self):
|
| 149 |
+
"""
|
| 150 |
+
Returns the number of independent generators of A_n
|
| 151 |
+
"""
|
| 152 |
+
n = self.n
|
| 153 |
+
return n**2 - 1
|
| 154 |
+
|
| 155 |
+
def lie_algebra(self):
|
| 156 |
+
"""
|
| 157 |
+
Returns the Lie algebra associated with A_n
|
| 158 |
+
"""
|
| 159 |
+
n = self.n
|
| 160 |
+
return "su(" + str(n + 1) + ")"
|
| 161 |
+
|
| 162 |
+
def dynkin_diagram(self):
|
| 163 |
+
n = self.n
|
| 164 |
+
diag = "---".join("0" for i in range(1, n+1)) + "\n"
|
| 165 |
+
diag += " ".join(str(i) for i in range(1, n+1))
|
| 166 |
+
return diag
|
lib/python3.12/site-packages/sympy/liealgebras/type_b.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import Standard_Cartan
|
| 2 |
+
from sympy.core.backend import eye
|
| 3 |
+
|
| 4 |
+
class TypeB(Standard_Cartan):
|
| 5 |
+
|
| 6 |
+
def __new__(cls, n):
|
| 7 |
+
if n < 2:
|
| 8 |
+
raise ValueError("n cannot be less than 2")
|
| 9 |
+
return Standard_Cartan.__new__(cls, "B", n)
|
| 10 |
+
|
| 11 |
+
def dimension(self):
|
| 12 |
+
"""Dimension of the vector space V underlying the Lie algebra
|
| 13 |
+
|
| 14 |
+
Examples
|
| 15 |
+
========
|
| 16 |
+
|
| 17 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 18 |
+
>>> c = CartanType("B3")
|
| 19 |
+
>>> c.dimension()
|
| 20 |
+
3
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
return self.n
|
| 24 |
+
|
| 25 |
+
def basic_root(self, i, j):
|
| 26 |
+
"""
|
| 27 |
+
This is a method just to generate roots
|
| 28 |
+
with a 1 iin the ith position and a -1
|
| 29 |
+
in the jth position.
|
| 30 |
+
|
| 31 |
+
"""
|
| 32 |
+
root = [0]*self.n
|
| 33 |
+
root[i] = 1
|
| 34 |
+
root[j] = -1
|
| 35 |
+
return root
|
| 36 |
+
|
| 37 |
+
def simple_root(self, i):
|
| 38 |
+
"""
|
| 39 |
+
Every lie algebra has a unique root system.
|
| 40 |
+
Given a root system Q, there is a subset of the
|
| 41 |
+
roots such that an element of Q is called a
|
| 42 |
+
simple root if it cannot be written as the sum
|
| 43 |
+
of two elements in Q. If we let D denote the
|
| 44 |
+
set of simple roots, then it is clear that every
|
| 45 |
+
element of Q can be written as a linear combination
|
| 46 |
+
of elements of D with all coefficients non-negative.
|
| 47 |
+
|
| 48 |
+
In B_n the first n-1 simple roots are the same as the
|
| 49 |
+
roots in A_(n-1) (a 1 in the ith position, a -1 in
|
| 50 |
+
the (i+1)th position, and zeroes elsewhere). The n-th
|
| 51 |
+
simple root is the root with a 1 in the nth position
|
| 52 |
+
and zeroes elsewhere.
|
| 53 |
+
|
| 54 |
+
This method returns the ith simple root for the B series.
|
| 55 |
+
|
| 56 |
+
Examples
|
| 57 |
+
========
|
| 58 |
+
|
| 59 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 60 |
+
>>> c = CartanType("B3")
|
| 61 |
+
>>> c.simple_root(2)
|
| 62 |
+
[0, 1, -1]
|
| 63 |
+
|
| 64 |
+
"""
|
| 65 |
+
n = self.n
|
| 66 |
+
if i < n:
|
| 67 |
+
return self.basic_root(i-1, i)
|
| 68 |
+
else:
|
| 69 |
+
root = [0]*self.n
|
| 70 |
+
root[n-1] = 1
|
| 71 |
+
return root
|
| 72 |
+
|
| 73 |
+
def positive_roots(self):
|
| 74 |
+
"""
|
| 75 |
+
This method generates all the positive roots of
|
| 76 |
+
A_n. This is half of all of the roots of B_n;
|
| 77 |
+
by multiplying all the positive roots by -1 we
|
| 78 |
+
get the negative roots.
|
| 79 |
+
|
| 80 |
+
Examples
|
| 81 |
+
========
|
| 82 |
+
|
| 83 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 84 |
+
>>> c = CartanType("A3")
|
| 85 |
+
>>> c.positive_roots()
|
| 86 |
+
{1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0],
|
| 87 |
+
5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
n = self.n
|
| 91 |
+
posroots = {}
|
| 92 |
+
k = 0
|
| 93 |
+
for i in range(0, n-1):
|
| 94 |
+
for j in range(i+1, n):
|
| 95 |
+
k += 1
|
| 96 |
+
posroots[k] = self.basic_root(i, j)
|
| 97 |
+
k += 1
|
| 98 |
+
root = self.basic_root(i, j)
|
| 99 |
+
root[j] = 1
|
| 100 |
+
posroots[k] = root
|
| 101 |
+
|
| 102 |
+
for i in range(0, n):
|
| 103 |
+
k += 1
|
| 104 |
+
root = [0]*n
|
| 105 |
+
root[i] = 1
|
| 106 |
+
posroots[k] = root
|
| 107 |
+
|
| 108 |
+
return posroots
|
| 109 |
+
|
| 110 |
+
def roots(self):
|
| 111 |
+
"""
|
| 112 |
+
Returns the total number of roots for B_n"
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
n = self.n
|
| 116 |
+
return 2*(n**2)
|
| 117 |
+
|
| 118 |
+
def cartan_matrix(self):
|
| 119 |
+
"""
|
| 120 |
+
Returns the Cartan matrix for B_n.
|
| 121 |
+
The Cartan matrix matrix for a Lie algebra is
|
| 122 |
+
generated by assigning an ordering to the simple
|
| 123 |
+
roots, (alpha[1], ...., alpha[l]). Then the ijth
|
| 124 |
+
entry of the Cartan matrix is (<alpha[i],alpha[j]>).
|
| 125 |
+
|
| 126 |
+
Examples
|
| 127 |
+
========
|
| 128 |
+
|
| 129 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 130 |
+
>>> c = CartanType('B4')
|
| 131 |
+
>>> c.cartan_matrix()
|
| 132 |
+
Matrix([
|
| 133 |
+
[ 2, -1, 0, 0],
|
| 134 |
+
[-1, 2, -1, 0],
|
| 135 |
+
[ 0, -1, 2, -2],
|
| 136 |
+
[ 0, 0, -1, 2]])
|
| 137 |
+
|
| 138 |
+
"""
|
| 139 |
+
|
| 140 |
+
n = self.n
|
| 141 |
+
m = 2* eye(n)
|
| 142 |
+
i = 1
|
| 143 |
+
while i < n-1:
|
| 144 |
+
m[i, i+1] = -1
|
| 145 |
+
m[i, i-1] = -1
|
| 146 |
+
i += 1
|
| 147 |
+
m[0, 1] = -1
|
| 148 |
+
m[n-2, n-1] = -2
|
| 149 |
+
m[n-1, n-2] = -1
|
| 150 |
+
return m
|
| 151 |
+
|
| 152 |
+
def basis(self):
|
| 153 |
+
"""
|
| 154 |
+
Returns the number of independent generators of B_n
|
| 155 |
+
"""
|
| 156 |
+
|
| 157 |
+
n = self.n
|
| 158 |
+
return (n**2 - n)/2
|
| 159 |
+
|
| 160 |
+
def lie_algebra(self):
|
| 161 |
+
"""
|
| 162 |
+
Returns the Lie algebra associated with B_n
|
| 163 |
+
"""
|
| 164 |
+
|
| 165 |
+
n = self.n
|
| 166 |
+
return "so(" + str(2*n) + ")"
|
| 167 |
+
|
| 168 |
+
def dynkin_diagram(self):
|
| 169 |
+
n = self.n
|
| 170 |
+
diag = "---".join("0" for i in range(1, n)) + "=>=0\n"
|
| 171 |
+
diag += " ".join(str(i) for i in range(1, n+1))
|
| 172 |
+
return diag
|
lib/python3.12/site-packages/sympy/liealgebras/type_c.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import Standard_Cartan
|
| 2 |
+
from sympy.core.backend import eye
|
| 3 |
+
|
| 4 |
+
class TypeC(Standard_Cartan):
|
| 5 |
+
|
| 6 |
+
def __new__(cls, n):
|
| 7 |
+
if n < 3:
|
| 8 |
+
raise ValueError("n cannot be less than 3")
|
| 9 |
+
return Standard_Cartan.__new__(cls, "C", n)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def dimension(self):
|
| 13 |
+
"""Dimension of the vector space V underlying the Lie algebra
|
| 14 |
+
|
| 15 |
+
Examples
|
| 16 |
+
========
|
| 17 |
+
|
| 18 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 19 |
+
>>> c = CartanType("C3")
|
| 20 |
+
>>> c.dimension()
|
| 21 |
+
3
|
| 22 |
+
"""
|
| 23 |
+
n = self.n
|
| 24 |
+
return n
|
| 25 |
+
|
| 26 |
+
def basic_root(self, i, j):
|
| 27 |
+
"""Generate roots with 1 in ith position and a -1 in jth position
|
| 28 |
+
"""
|
| 29 |
+
n = self.n
|
| 30 |
+
root = [0]*n
|
| 31 |
+
root[i] = 1
|
| 32 |
+
root[j] = -1
|
| 33 |
+
return root
|
| 34 |
+
|
| 35 |
+
def simple_root(self, i):
|
| 36 |
+
"""The ith simple root for the C series
|
| 37 |
+
|
| 38 |
+
Every lie algebra has a unique root system.
|
| 39 |
+
Given a root system Q, there is a subset of the
|
| 40 |
+
roots such that an element of Q is called a
|
| 41 |
+
simple root if it cannot be written as the sum
|
| 42 |
+
of two elements in Q. If we let D denote the
|
| 43 |
+
set of simple roots, then it is clear that every
|
| 44 |
+
element of Q can be written as a linear combination
|
| 45 |
+
of elements of D with all coefficients non-negative.
|
| 46 |
+
|
| 47 |
+
In C_n, the first n-1 simple roots are the same as
|
| 48 |
+
the roots in A_(n-1) (a 1 in the ith position, a -1
|
| 49 |
+
in the (i+1)th position, and zeroes elsewhere). The
|
| 50 |
+
nth simple root is the root in which there is a 2 in
|
| 51 |
+
the nth position and zeroes elsewhere.
|
| 52 |
+
|
| 53 |
+
Examples
|
| 54 |
+
========
|
| 55 |
+
|
| 56 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 57 |
+
>>> c = CartanType("C3")
|
| 58 |
+
>>> c.simple_root(2)
|
| 59 |
+
[0, 1, -1]
|
| 60 |
+
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
n = self.n
|
| 64 |
+
if i < n:
|
| 65 |
+
return self.basic_root(i-1,i)
|
| 66 |
+
else:
|
| 67 |
+
root = [0]*self.n
|
| 68 |
+
root[n-1] = 2
|
| 69 |
+
return root
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def positive_roots(self):
|
| 73 |
+
"""Generates all the positive roots of A_n
|
| 74 |
+
|
| 75 |
+
This is half of all of the roots of C_n; by multiplying all the
|
| 76 |
+
positive roots by -1 we get the negative roots.
|
| 77 |
+
|
| 78 |
+
Examples
|
| 79 |
+
========
|
| 80 |
+
|
| 81 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 82 |
+
>>> c = CartanType("A3")
|
| 83 |
+
>>> c.positive_roots()
|
| 84 |
+
{1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0],
|
| 85 |
+
5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
| 86 |
+
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
n = self.n
|
| 90 |
+
posroots = {}
|
| 91 |
+
k = 0
|
| 92 |
+
for i in range(0, n-1):
|
| 93 |
+
for j in range(i+1, n):
|
| 94 |
+
k += 1
|
| 95 |
+
posroots[k] = self.basic_root(i, j)
|
| 96 |
+
k += 1
|
| 97 |
+
root = self.basic_root(i, j)
|
| 98 |
+
root[j] = 1
|
| 99 |
+
posroots[k] = root
|
| 100 |
+
|
| 101 |
+
for i in range(0, n):
|
| 102 |
+
k += 1
|
| 103 |
+
root = [0]*n
|
| 104 |
+
root[i] = 2
|
| 105 |
+
posroots[k] = root
|
| 106 |
+
|
| 107 |
+
return posroots
|
| 108 |
+
|
| 109 |
+
def roots(self):
|
| 110 |
+
"""
|
| 111 |
+
Returns the total number of roots for C_n"
|
| 112 |
+
"""
|
| 113 |
+
|
| 114 |
+
n = self.n
|
| 115 |
+
return 2*(n**2)
|
| 116 |
+
|
| 117 |
+
def cartan_matrix(self):
|
| 118 |
+
"""The Cartan matrix for C_n
|
| 119 |
+
|
| 120 |
+
The Cartan matrix matrix for a Lie algebra is
|
| 121 |
+
generated by assigning an ordering to the simple
|
| 122 |
+
roots, (alpha[1], ...., alpha[l]). Then the ijth
|
| 123 |
+
entry of the Cartan matrix is (<alpha[i],alpha[j]>).
|
| 124 |
+
|
| 125 |
+
Examples
|
| 126 |
+
========
|
| 127 |
+
|
| 128 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 129 |
+
>>> c = CartanType('C4')
|
| 130 |
+
>>> c.cartan_matrix()
|
| 131 |
+
Matrix([
|
| 132 |
+
[ 2, -1, 0, 0],
|
| 133 |
+
[-1, 2, -1, 0],
|
| 134 |
+
[ 0, -1, 2, -1],
|
| 135 |
+
[ 0, 0, -2, 2]])
|
| 136 |
+
|
| 137 |
+
"""
|
| 138 |
+
|
| 139 |
+
n = self.n
|
| 140 |
+
m = 2 * eye(n)
|
| 141 |
+
i = 1
|
| 142 |
+
while i < n-1:
|
| 143 |
+
m[i, i+1] = -1
|
| 144 |
+
m[i, i-1] = -1
|
| 145 |
+
i += 1
|
| 146 |
+
m[0,1] = -1
|
| 147 |
+
m[n-1, n-2] = -2
|
| 148 |
+
return m
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def basis(self):
|
| 152 |
+
"""
|
| 153 |
+
Returns the number of independent generators of C_n
|
| 154 |
+
"""
|
| 155 |
+
|
| 156 |
+
n = self.n
|
| 157 |
+
return n*(2*n + 1)
|
| 158 |
+
|
| 159 |
+
def lie_algebra(self):
|
| 160 |
+
"""
|
| 161 |
+
Returns the Lie algebra associated with C_n"
|
| 162 |
+
"""
|
| 163 |
+
|
| 164 |
+
n = self.n
|
| 165 |
+
return "sp(" + str(2*n) + ")"
|
| 166 |
+
|
| 167 |
+
def dynkin_diagram(self):
|
| 168 |
+
n = self.n
|
| 169 |
+
diag = "---".join("0" for i in range(1, n)) + "=<=0\n"
|
| 170 |
+
diag += " ".join(str(i) for i in range(1, n+1))
|
| 171 |
+
return diag
|
lib/python3.12/site-packages/sympy/liealgebras/type_d.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import Standard_Cartan
|
| 2 |
+
from sympy.core.backend import eye
|
| 3 |
+
|
| 4 |
+
class TypeD(Standard_Cartan):
|
| 5 |
+
|
| 6 |
+
def __new__(cls, n):
|
| 7 |
+
if n < 3:
|
| 8 |
+
raise ValueError("n cannot be less than 3")
|
| 9 |
+
return Standard_Cartan.__new__(cls, "D", n)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def dimension(self):
|
| 13 |
+
"""Dmension of the vector space V underlying the Lie algebra
|
| 14 |
+
|
| 15 |
+
Examples
|
| 16 |
+
========
|
| 17 |
+
|
| 18 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 19 |
+
>>> c = CartanType("D4")
|
| 20 |
+
>>> c.dimension()
|
| 21 |
+
4
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
return self.n
|
| 25 |
+
|
| 26 |
+
def basic_root(self, i, j):
|
| 27 |
+
"""
|
| 28 |
+
This is a method just to generate roots
|
| 29 |
+
with a 1 iin the ith position and a -1
|
| 30 |
+
in the jth position.
|
| 31 |
+
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
n = self.n
|
| 35 |
+
root = [0]*n
|
| 36 |
+
root[i] = 1
|
| 37 |
+
root[j] = -1
|
| 38 |
+
return root
|
| 39 |
+
|
| 40 |
+
def simple_root(self, i):
|
| 41 |
+
"""
|
| 42 |
+
Every lie algebra has a unique root system.
|
| 43 |
+
Given a root system Q, there is a subset of the
|
| 44 |
+
roots such that an element of Q is called a
|
| 45 |
+
simple root if it cannot be written as the sum
|
| 46 |
+
of two elements in Q. If we let D denote the
|
| 47 |
+
set of simple roots, then it is clear that every
|
| 48 |
+
element of Q can be written as a linear combination
|
| 49 |
+
of elements of D with all coefficients non-negative.
|
| 50 |
+
|
| 51 |
+
In D_n, the first n-1 simple roots are the same as
|
| 52 |
+
the roots in A_(n-1) (a 1 in the ith position, a -1
|
| 53 |
+
in the (i+1)th position, and zeroes elsewhere).
|
| 54 |
+
The nth simple root is the root in which there 1s in
|
| 55 |
+
the nth and (n-1)th positions, and zeroes elsewhere.
|
| 56 |
+
|
| 57 |
+
This method returns the ith simple root for the D series.
|
| 58 |
+
|
| 59 |
+
Examples
|
| 60 |
+
========
|
| 61 |
+
|
| 62 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 63 |
+
>>> c = CartanType("D4")
|
| 64 |
+
>>> c.simple_root(2)
|
| 65 |
+
[0, 1, -1, 0]
|
| 66 |
+
|
| 67 |
+
"""
|
| 68 |
+
|
| 69 |
+
n = self.n
|
| 70 |
+
if i < n:
|
| 71 |
+
return self.basic_root(i-1, i)
|
| 72 |
+
else:
|
| 73 |
+
root = [0]*n
|
| 74 |
+
root[n-2] = 1
|
| 75 |
+
root[n-1] = 1
|
| 76 |
+
return root
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def positive_roots(self):
|
| 80 |
+
"""
|
| 81 |
+
This method generates all the positive roots of
|
| 82 |
+
A_n. This is half of all of the roots of D_n
|
| 83 |
+
by multiplying all the positive roots by -1 we
|
| 84 |
+
get the negative roots.
|
| 85 |
+
|
| 86 |
+
Examples
|
| 87 |
+
========
|
| 88 |
+
|
| 89 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 90 |
+
>>> c = CartanType("A3")
|
| 91 |
+
>>> c.positive_roots()
|
| 92 |
+
{1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0],
|
| 93 |
+
5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
| 94 |
+
"""
|
| 95 |
+
|
| 96 |
+
n = self.n
|
| 97 |
+
posroots = {}
|
| 98 |
+
k = 0
|
| 99 |
+
for i in range(0, n-1):
|
| 100 |
+
for j in range(i+1, n):
|
| 101 |
+
k += 1
|
| 102 |
+
posroots[k] = self.basic_root(i, j)
|
| 103 |
+
k += 1
|
| 104 |
+
root = self.basic_root(i, j)
|
| 105 |
+
root[j] = 1
|
| 106 |
+
posroots[k] = root
|
| 107 |
+
return posroots
|
| 108 |
+
|
| 109 |
+
def roots(self):
|
| 110 |
+
"""
|
| 111 |
+
Returns the total number of roots for D_n"
|
| 112 |
+
"""
|
| 113 |
+
|
| 114 |
+
n = self.n
|
| 115 |
+
return 2*n*(n-1)
|
| 116 |
+
|
| 117 |
+
def cartan_matrix(self):
|
| 118 |
+
"""
|
| 119 |
+
Returns the Cartan matrix for D_n.
|
| 120 |
+
The Cartan matrix matrix for a Lie algebra is
|
| 121 |
+
generated by assigning an ordering to the simple
|
| 122 |
+
roots, (alpha[1], ...., alpha[l]). Then the ijth
|
| 123 |
+
entry of the Cartan matrix is (<alpha[i],alpha[j]>).
|
| 124 |
+
|
| 125 |
+
Examples
|
| 126 |
+
========
|
| 127 |
+
|
| 128 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 129 |
+
>>> c = CartanType('D4')
|
| 130 |
+
>>> c.cartan_matrix()
|
| 131 |
+
Matrix([
|
| 132 |
+
[ 2, -1, 0, 0],
|
| 133 |
+
[-1, 2, -1, -1],
|
| 134 |
+
[ 0, -1, 2, 0],
|
| 135 |
+
[ 0, -1, 0, 2]])
|
| 136 |
+
|
| 137 |
+
"""
|
| 138 |
+
|
| 139 |
+
n = self.n
|
| 140 |
+
m = 2*eye(n)
|
| 141 |
+
i = 1
|
| 142 |
+
while i < n-2:
|
| 143 |
+
m[i,i+1] = -1
|
| 144 |
+
m[i,i-1] = -1
|
| 145 |
+
i += 1
|
| 146 |
+
m[n-2, n-3] = -1
|
| 147 |
+
m[n-3, n-1] = -1
|
| 148 |
+
m[n-1, n-3] = -1
|
| 149 |
+
m[0, 1] = -1
|
| 150 |
+
return m
|
| 151 |
+
|
| 152 |
+
def basis(self):
|
| 153 |
+
"""
|
| 154 |
+
Returns the number of independent generators of D_n
|
| 155 |
+
"""
|
| 156 |
+
n = self.n
|
| 157 |
+
return n*(n-1)/2
|
| 158 |
+
|
| 159 |
+
def lie_algebra(self):
|
| 160 |
+
"""
|
| 161 |
+
Returns the Lie algebra associated with D_n"
|
| 162 |
+
"""
|
| 163 |
+
|
| 164 |
+
n = self.n
|
| 165 |
+
return "so(" + str(2*n) + ")"
|
| 166 |
+
|
| 167 |
+
def dynkin_diagram(self):
|
| 168 |
+
n = self.n
|
| 169 |
+
diag = " "*4*(n-3) + str(n-1) + "\n"
|
| 170 |
+
diag += " "*4*(n-3) + "0\n"
|
| 171 |
+
diag += " "*4*(n-3) +"|\n"
|
| 172 |
+
diag += " "*4*(n-3) + "|\n"
|
| 173 |
+
diag += "---".join("0" for i in range(1,n)) + "\n"
|
| 174 |
+
diag += " ".join(str(i) for i in range(1, n-1)) + " "+str(n)
|
| 175 |
+
return diag
|
lib/python3.12/site-packages/sympy/liealgebras/type_e.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import Standard_Cartan
|
| 2 |
+
from sympy.core.backend import eye, Rational
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class TypeE(Standard_Cartan):
|
| 6 |
+
|
| 7 |
+
def __new__(cls, n):
|
| 8 |
+
if n < 6 or n > 8:
|
| 9 |
+
raise ValueError("Invalid value of n")
|
| 10 |
+
return Standard_Cartan.__new__(cls, "E", n)
|
| 11 |
+
|
| 12 |
+
def dimension(self):
|
| 13 |
+
"""Dimension of the vector space V underlying the Lie algebra
|
| 14 |
+
|
| 15 |
+
Examples
|
| 16 |
+
========
|
| 17 |
+
|
| 18 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 19 |
+
>>> c = CartanType("E6")
|
| 20 |
+
>>> c.dimension()
|
| 21 |
+
8
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
return 8
|
| 25 |
+
|
| 26 |
+
def basic_root(self, i, j):
|
| 27 |
+
"""
|
| 28 |
+
This is a method just to generate roots
|
| 29 |
+
with a -1 in the ith position and a 1
|
| 30 |
+
in the jth position.
|
| 31 |
+
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
root = [0]*8
|
| 35 |
+
root[i] = -1
|
| 36 |
+
root[j] = 1
|
| 37 |
+
return root
|
| 38 |
+
|
| 39 |
+
def simple_root(self, i):
|
| 40 |
+
"""
|
| 41 |
+
Every lie algebra has a unique root system.
|
| 42 |
+
Given a root system Q, there is a subset of the
|
| 43 |
+
roots such that an element of Q is called a
|
| 44 |
+
simple root if it cannot be written as the sum
|
| 45 |
+
of two elements in Q. If we let D denote the
|
| 46 |
+
set of simple roots, then it is clear that every
|
| 47 |
+
element of Q can be written as a linear combination
|
| 48 |
+
of elements of D with all coefficients non-negative.
|
| 49 |
+
|
| 50 |
+
This method returns the ith simple root for E_n.
|
| 51 |
+
|
| 52 |
+
Examples
|
| 53 |
+
========
|
| 54 |
+
|
| 55 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 56 |
+
>>> c = CartanType("E6")
|
| 57 |
+
>>> c.simple_root(2)
|
| 58 |
+
[1, 1, 0, 0, 0, 0, 0, 0]
|
| 59 |
+
"""
|
| 60 |
+
n = self.n
|
| 61 |
+
if i == 1:
|
| 62 |
+
root = [-0.5]*8
|
| 63 |
+
root[0] = 0.5
|
| 64 |
+
root[7] = 0.5
|
| 65 |
+
return root
|
| 66 |
+
elif i == 2:
|
| 67 |
+
root = [0]*8
|
| 68 |
+
root[1] = 1
|
| 69 |
+
root[0] = 1
|
| 70 |
+
return root
|
| 71 |
+
else:
|
| 72 |
+
if i in (7, 8) and n == 6:
|
| 73 |
+
raise ValueError("E6 only has six simple roots!")
|
| 74 |
+
if i == 8 and n == 7:
|
| 75 |
+
raise ValueError("E7 has only 7 simple roots!")
|
| 76 |
+
|
| 77 |
+
return self.basic_root(i - 3, i - 2)
|
| 78 |
+
|
| 79 |
+
def positive_roots(self):
|
| 80 |
+
"""
|
| 81 |
+
This method generates all the positive roots of
|
| 82 |
+
A_n. This is half of all of the roots of E_n;
|
| 83 |
+
by multiplying all the positive roots by -1 we
|
| 84 |
+
get the negative roots.
|
| 85 |
+
|
| 86 |
+
Examples
|
| 87 |
+
========
|
| 88 |
+
|
| 89 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 90 |
+
>>> c = CartanType("A3")
|
| 91 |
+
>>> c.positive_roots()
|
| 92 |
+
{1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0],
|
| 93 |
+
5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
| 94 |
+
"""
|
| 95 |
+
n = self.n
|
| 96 |
+
if n == 6:
|
| 97 |
+
posroots = {}
|
| 98 |
+
k = 0
|
| 99 |
+
for i in range(n-1):
|
| 100 |
+
for j in range(i+1, n-1):
|
| 101 |
+
k += 1
|
| 102 |
+
root = self.basic_root(i, j)
|
| 103 |
+
posroots[k] = root
|
| 104 |
+
k += 1
|
| 105 |
+
root = self.basic_root(i, j)
|
| 106 |
+
root[i] = 1
|
| 107 |
+
posroots[k] = root
|
| 108 |
+
|
| 109 |
+
root = [Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2),
|
| 110 |
+
Rational(-1, 2), Rational(-1, 2), Rational(1, 2)]
|
| 111 |
+
for a in range(0, 2):
|
| 112 |
+
for b in range(0, 2):
|
| 113 |
+
for c in range(0, 2):
|
| 114 |
+
for d in range(0, 2):
|
| 115 |
+
for e in range(0, 2):
|
| 116 |
+
if (a + b + c + d + e)%2 == 0:
|
| 117 |
+
k += 1
|
| 118 |
+
if a == 1:
|
| 119 |
+
root[0] = Rational(-1, 2)
|
| 120 |
+
if b == 1:
|
| 121 |
+
root[1] = Rational(-1, 2)
|
| 122 |
+
if c == 1:
|
| 123 |
+
root[2] = Rational(-1, 2)
|
| 124 |
+
if d == 1:
|
| 125 |
+
root[3] = Rational(-1, 2)
|
| 126 |
+
if e == 1:
|
| 127 |
+
root[4] = Rational(-1, 2)
|
| 128 |
+
posroots[k] = root
|
| 129 |
+
|
| 130 |
+
return posroots
|
| 131 |
+
if n == 7:
|
| 132 |
+
posroots = {}
|
| 133 |
+
k = 0
|
| 134 |
+
for i in range(n-1):
|
| 135 |
+
for j in range(i+1, n-1):
|
| 136 |
+
k += 1
|
| 137 |
+
root = self.basic_root(i, j)
|
| 138 |
+
posroots[k] = root
|
| 139 |
+
k += 1
|
| 140 |
+
root = self.basic_root(i, j)
|
| 141 |
+
root[i] = 1
|
| 142 |
+
posroots[k] = root
|
| 143 |
+
|
| 144 |
+
k += 1
|
| 145 |
+
posroots[k] = [0, 0, 0, 0, 0, 1, 1, 0]
|
| 146 |
+
root = [Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2),
|
| 147 |
+
Rational(-1, 2), Rational(-1, 2), Rational(1, 2)]
|
| 148 |
+
for a in range(0, 2):
|
| 149 |
+
for b in range(0, 2):
|
| 150 |
+
for c in range(0, 2):
|
| 151 |
+
for d in range(0, 2):
|
| 152 |
+
for e in range(0, 2):
|
| 153 |
+
for f in range(0, 2):
|
| 154 |
+
if (a + b + c + d + e + f)%2 == 0:
|
| 155 |
+
k += 1
|
| 156 |
+
if a == 1:
|
| 157 |
+
root[0] = Rational(-1, 2)
|
| 158 |
+
if b == 1:
|
| 159 |
+
root[1] = Rational(-1, 2)
|
| 160 |
+
if c == 1:
|
| 161 |
+
root[2] = Rational(-1, 2)
|
| 162 |
+
if d == 1:
|
| 163 |
+
root[3] = Rational(-1, 2)
|
| 164 |
+
if e == 1:
|
| 165 |
+
root[4] = Rational(-1, 2)
|
| 166 |
+
if f == 1:
|
| 167 |
+
root[5] = Rational(1, 2)
|
| 168 |
+
posroots[k] = root
|
| 169 |
+
|
| 170 |
+
return posroots
|
| 171 |
+
if n == 8:
|
| 172 |
+
posroots = {}
|
| 173 |
+
k = 0
|
| 174 |
+
for i in range(n):
|
| 175 |
+
for j in range(i+1, n):
|
| 176 |
+
k += 1
|
| 177 |
+
root = self.basic_root(i, j)
|
| 178 |
+
posroots[k] = root
|
| 179 |
+
k += 1
|
| 180 |
+
root = self.basic_root(i, j)
|
| 181 |
+
root[i] = 1
|
| 182 |
+
posroots[k] = root
|
| 183 |
+
|
| 184 |
+
root = [Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2),
|
| 185 |
+
Rational(-1, 2), Rational(-1, 2), Rational(1, 2)]
|
| 186 |
+
for a in range(0, 2):
|
| 187 |
+
for b in range(0, 2):
|
| 188 |
+
for c in range(0, 2):
|
| 189 |
+
for d in range(0, 2):
|
| 190 |
+
for e in range(0, 2):
|
| 191 |
+
for f in range(0, 2):
|
| 192 |
+
for g in range(0, 2):
|
| 193 |
+
if (a + b + c + d + e + f + g)%2 == 0:
|
| 194 |
+
k += 1
|
| 195 |
+
if a == 1:
|
| 196 |
+
root[0] = Rational(-1, 2)
|
| 197 |
+
if b == 1:
|
| 198 |
+
root[1] = Rational(-1, 2)
|
| 199 |
+
if c == 1:
|
| 200 |
+
root[2] = Rational(-1, 2)
|
| 201 |
+
if d == 1:
|
| 202 |
+
root[3] = Rational(-1, 2)
|
| 203 |
+
if e == 1:
|
| 204 |
+
root[4] = Rational(-1, 2)
|
| 205 |
+
if f == 1:
|
| 206 |
+
root[5] = Rational(1, 2)
|
| 207 |
+
if g == 1:
|
| 208 |
+
root[6] = Rational(1, 2)
|
| 209 |
+
posroots[k] = root
|
| 210 |
+
|
| 211 |
+
return posroots
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def roots(self):
|
| 216 |
+
"""
|
| 217 |
+
Returns the total number of roots of E_n
|
| 218 |
+
"""
|
| 219 |
+
|
| 220 |
+
n = self.n
|
| 221 |
+
if n == 6:
|
| 222 |
+
return 72
|
| 223 |
+
if n == 7:
|
| 224 |
+
return 126
|
| 225 |
+
if n == 8:
|
| 226 |
+
return 240
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def cartan_matrix(self):
|
| 230 |
+
"""
|
| 231 |
+
Returns the Cartan matrix for G_2
|
| 232 |
+
The Cartan matrix matrix for a Lie algebra is
|
| 233 |
+
generated by assigning an ordering to the simple
|
| 234 |
+
roots, (alpha[1], ...., alpha[l]). Then the ijth
|
| 235 |
+
entry of the Cartan matrix is (<alpha[i],alpha[j]>).
|
| 236 |
+
|
| 237 |
+
Examples
|
| 238 |
+
========
|
| 239 |
+
|
| 240 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 241 |
+
>>> c = CartanType('A4')
|
| 242 |
+
>>> c.cartan_matrix()
|
| 243 |
+
Matrix([
|
| 244 |
+
[ 2, -1, 0, 0],
|
| 245 |
+
[-1, 2, -1, 0],
|
| 246 |
+
[ 0, -1, 2, -1],
|
| 247 |
+
[ 0, 0, -1, 2]])
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
"""
|
| 251 |
+
|
| 252 |
+
n = self.n
|
| 253 |
+
m = 2*eye(n)
|
| 254 |
+
i = 3
|
| 255 |
+
while i < n-1:
|
| 256 |
+
m[i, i+1] = -1
|
| 257 |
+
m[i, i-1] = -1
|
| 258 |
+
i += 1
|
| 259 |
+
m[0, 2] = m[2, 0] = -1
|
| 260 |
+
m[1, 3] = m[3, 1] = -1
|
| 261 |
+
m[2, 3] = -1
|
| 262 |
+
m[n-1, n-2] = -1
|
| 263 |
+
return m
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
def basis(self):
|
| 267 |
+
"""
|
| 268 |
+
Returns the number of independent generators of E_n
|
| 269 |
+
"""
|
| 270 |
+
|
| 271 |
+
n = self.n
|
| 272 |
+
if n == 6:
|
| 273 |
+
return 78
|
| 274 |
+
if n == 7:
|
| 275 |
+
return 133
|
| 276 |
+
if n == 8:
|
| 277 |
+
return 248
|
| 278 |
+
|
| 279 |
+
def dynkin_diagram(self):
|
| 280 |
+
n = self.n
|
| 281 |
+
diag = " "*8 + str(2) + "\n"
|
| 282 |
+
diag += " "*8 + "0\n"
|
| 283 |
+
diag += " "*8 + "|\n"
|
| 284 |
+
diag += " "*8 + "|\n"
|
| 285 |
+
diag += "---".join("0" for i in range(1, n)) + "\n"
|
| 286 |
+
diag += "1 " + " ".join(str(i) for i in range(3, n+1))
|
| 287 |
+
return diag
|
lib/python3.12/site-packages/sympy/liealgebras/type_f.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .cartan_type import Standard_Cartan
|
| 2 |
+
from sympy.core.backend import Matrix, Rational
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class TypeF(Standard_Cartan):
|
| 6 |
+
|
| 7 |
+
def __new__(cls, n):
|
| 8 |
+
if n != 4:
|
| 9 |
+
raise ValueError("n should be 4")
|
| 10 |
+
return Standard_Cartan.__new__(cls, "F", 4)
|
| 11 |
+
|
| 12 |
+
def dimension(self):
|
| 13 |
+
"""Dimension of the vector space V underlying the Lie algebra
|
| 14 |
+
|
| 15 |
+
Examples
|
| 16 |
+
========
|
| 17 |
+
|
| 18 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 19 |
+
>>> c = CartanType("F4")
|
| 20 |
+
>>> c.dimension()
|
| 21 |
+
4
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
return 4
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def basic_root(self, i, j):
|
| 28 |
+
"""Generate roots with 1 in ith position and -1 in jth position
|
| 29 |
+
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
n = self.n
|
| 33 |
+
root = [0]*n
|
| 34 |
+
root[i] = 1
|
| 35 |
+
root[j] = -1
|
| 36 |
+
return root
|
| 37 |
+
|
| 38 |
+
def simple_root(self, i):
|
| 39 |
+
"""The ith simple root of F_4
|
| 40 |
+
|
| 41 |
+
Every lie algebra has a unique root system.
|
| 42 |
+
Given a root system Q, there is a subset of the
|
| 43 |
+
roots such that an element of Q is called a
|
| 44 |
+
simple root if it cannot be written as the sum
|
| 45 |
+
of two elements in Q. If we let D denote the
|
| 46 |
+
set of simple roots, then it is clear that every
|
| 47 |
+
element of Q can be written as a linear combination
|
| 48 |
+
of elements of D with all coefficients non-negative.
|
| 49 |
+
|
| 50 |
+
Examples
|
| 51 |
+
========
|
| 52 |
+
|
| 53 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 54 |
+
>>> c = CartanType("F4")
|
| 55 |
+
>>> c.simple_root(3)
|
| 56 |
+
[0, 0, 0, 1]
|
| 57 |
+
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
if i < 3:
|
| 61 |
+
return self.basic_root(i-1, i)
|
| 62 |
+
if i == 3:
|
| 63 |
+
root = [0]*4
|
| 64 |
+
root[3] = 1
|
| 65 |
+
return root
|
| 66 |
+
if i == 4:
|
| 67 |
+
root = [Rational(-1, 2)]*4
|
| 68 |
+
return root
|
| 69 |
+
|
| 70 |
+
def positive_roots(self):
|
| 71 |
+
"""Generate all the positive roots of A_n
|
| 72 |
+
|
| 73 |
+
This is half of all of the roots of F_4; by multiplying all the
|
| 74 |
+
positive roots by -1 we get the negative roots.
|
| 75 |
+
|
| 76 |
+
Examples
|
| 77 |
+
========
|
| 78 |
+
|
| 79 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 80 |
+
>>> c = CartanType("A3")
|
| 81 |
+
>>> c.positive_roots()
|
| 82 |
+
{1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0],
|
| 83 |
+
5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
| 84 |
+
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
n = self.n
|
| 88 |
+
posroots = {}
|
| 89 |
+
k = 0
|
| 90 |
+
for i in range(0, n-1):
|
| 91 |
+
for j in range(i+1, n):
|
| 92 |
+
k += 1
|
| 93 |
+
posroots[k] = self.basic_root(i, j)
|
| 94 |
+
k += 1
|
| 95 |
+
root = self.basic_root(i, j)
|
| 96 |
+
root[j] = 1
|
| 97 |
+
posroots[k] = root
|
| 98 |
+
|
| 99 |
+
for i in range(0, n):
|
| 100 |
+
k += 1
|
| 101 |
+
root = [0]*n
|
| 102 |
+
root[i] = 1
|
| 103 |
+
posroots[k] = root
|
| 104 |
+
|
| 105 |
+
k += 1
|
| 106 |
+
root = [Rational(1, 2)]*n
|
| 107 |
+
posroots[k] = root
|
| 108 |
+
for i in range(1, 4):
|
| 109 |
+
k += 1
|
| 110 |
+
root = [Rational(1, 2)]*n
|
| 111 |
+
root[i] = Rational(-1, 2)
|
| 112 |
+
posroots[k] = root
|
| 113 |
+
|
| 114 |
+
posroots[k+1] = [Rational(1, 2), Rational(1, 2), Rational(-1, 2), Rational(-1, 2)]
|
| 115 |
+
posroots[k+2] = [Rational(1, 2), Rational(-1, 2), Rational(1, 2), Rational(-1, 2)]
|
| 116 |
+
posroots[k+3] = [Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(1, 2)]
|
| 117 |
+
posroots[k+4] = [Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(-1, 2)]
|
| 118 |
+
|
| 119 |
+
return posroots
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def roots(self):
|
| 123 |
+
"""
|
| 124 |
+
Returns the total number of roots for F_4
|
| 125 |
+
"""
|
| 126 |
+
return 48
|
| 127 |
+
|
| 128 |
+
def cartan_matrix(self):
|
| 129 |
+
"""The Cartan matrix for F_4
|
| 130 |
+
|
| 131 |
+
The Cartan matrix matrix for a Lie algebra is
|
| 132 |
+
generated by assigning an ordering to the simple
|
| 133 |
+
roots, (alpha[1], ...., alpha[l]). Then the ijth
|
| 134 |
+
entry of the Cartan matrix is (<alpha[i],alpha[j]>).
|
| 135 |
+
|
| 136 |
+
Examples
|
| 137 |
+
========
|
| 138 |
+
|
| 139 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 140 |
+
>>> c = CartanType('A4')
|
| 141 |
+
>>> c.cartan_matrix()
|
| 142 |
+
Matrix([
|
| 143 |
+
[ 2, -1, 0, 0],
|
| 144 |
+
[-1, 2, -1, 0],
|
| 145 |
+
[ 0, -1, 2, -1],
|
| 146 |
+
[ 0, 0, -1, 2]])
|
| 147 |
+
"""
|
| 148 |
+
|
| 149 |
+
m = Matrix( 4, 4, [2, -1, 0, 0, -1, 2, -2, 0, 0,
|
| 150 |
+
-1, 2, -1, 0, 0, -1, 2])
|
| 151 |
+
return m
|
| 152 |
+
|
| 153 |
+
def basis(self):
|
| 154 |
+
"""
|
| 155 |
+
Returns the number of independent generators of F_4
|
| 156 |
+
"""
|
| 157 |
+
return 52
|
| 158 |
+
|
| 159 |
+
def dynkin_diagram(self):
|
| 160 |
+
diag = "0---0=>=0---0\n"
|
| 161 |
+
diag += " ".join(str(i) for i in range(1, 5))
|
| 162 |
+
return diag
|
lib/python3.12/site-packages/sympy/liealgebras/type_g.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
from .cartan_type import Standard_Cartan
|
| 4 |
+
from sympy.core.backend import Matrix
|
| 5 |
+
|
| 6 |
+
class TypeG(Standard_Cartan):
|
| 7 |
+
|
| 8 |
+
def __new__(cls, n):
|
| 9 |
+
if n != 2:
|
| 10 |
+
raise ValueError("n should be 2")
|
| 11 |
+
return Standard_Cartan.__new__(cls, "G", 2)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def dimension(self):
|
| 15 |
+
"""Dimension of the vector space V underlying the Lie algebra
|
| 16 |
+
|
| 17 |
+
Examples
|
| 18 |
+
========
|
| 19 |
+
|
| 20 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 21 |
+
>>> c = CartanType("G2")
|
| 22 |
+
>>> c.dimension()
|
| 23 |
+
3
|
| 24 |
+
"""
|
| 25 |
+
return 3
|
| 26 |
+
|
| 27 |
+
def simple_root(self, i):
|
| 28 |
+
"""The ith simple root of G_2
|
| 29 |
+
|
| 30 |
+
Every lie algebra has a unique root system.
|
| 31 |
+
Given a root system Q, there is a subset of the
|
| 32 |
+
roots such that an element of Q is called a
|
| 33 |
+
simple root if it cannot be written as the sum
|
| 34 |
+
of two elements in Q. If we let D denote the
|
| 35 |
+
set of simple roots, then it is clear that every
|
| 36 |
+
element of Q can be written as a linear combination
|
| 37 |
+
of elements of D with all coefficients non-negative.
|
| 38 |
+
|
| 39 |
+
Examples
|
| 40 |
+
========
|
| 41 |
+
|
| 42 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 43 |
+
>>> c = CartanType("G2")
|
| 44 |
+
>>> c.simple_root(1)
|
| 45 |
+
[0, 1, -1]
|
| 46 |
+
|
| 47 |
+
"""
|
| 48 |
+
if i == 1:
|
| 49 |
+
return [0, 1, -1]
|
| 50 |
+
else:
|
| 51 |
+
return [1, -2, 1]
|
| 52 |
+
|
| 53 |
+
def positive_roots(self):
|
| 54 |
+
"""Generate all the positive roots of A_n
|
| 55 |
+
|
| 56 |
+
This is half of all of the roots of A_n; by multiplying all the
|
| 57 |
+
positive roots by -1 we get the negative roots.
|
| 58 |
+
|
| 59 |
+
Examples
|
| 60 |
+
========
|
| 61 |
+
|
| 62 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 63 |
+
>>> c = CartanType("A3")
|
| 64 |
+
>>> c.positive_roots()
|
| 65 |
+
{1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0],
|
| 66 |
+
5: [0, 1, 0, -1], 6: [0, 0, 1, -1]}
|
| 67 |
+
|
| 68 |
+
"""
|
| 69 |
+
|
| 70 |
+
roots = {1: [0, 1, -1], 2: [1, -2, 1], 3: [1, -1, 0], 4: [1, 0, 1],
|
| 71 |
+
5: [1, 1, -2], 6: [2, -1, -1]}
|
| 72 |
+
return roots
|
| 73 |
+
|
| 74 |
+
def roots(self):
|
| 75 |
+
"""
|
| 76 |
+
Returns the total number of roots of G_2"
|
| 77 |
+
"""
|
| 78 |
+
return 12
|
| 79 |
+
|
| 80 |
+
def cartan_matrix(self):
|
| 81 |
+
"""The Cartan matrix for G_2
|
| 82 |
+
|
| 83 |
+
The Cartan matrix matrix for a Lie algebra is
|
| 84 |
+
generated by assigning an ordering to the simple
|
| 85 |
+
roots, (alpha[1], ...., alpha[l]). Then the ijth
|
| 86 |
+
entry of the Cartan matrix is (<alpha[i],alpha[j]>).
|
| 87 |
+
|
| 88 |
+
Examples
|
| 89 |
+
========
|
| 90 |
+
|
| 91 |
+
>>> from sympy.liealgebras.cartan_type import CartanType
|
| 92 |
+
>>> c = CartanType("G2")
|
| 93 |
+
>>> c.cartan_matrix()
|
| 94 |
+
Matrix([
|
| 95 |
+
[ 2, -1],
|
| 96 |
+
[-3, 2]])
|
| 97 |
+
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
m = Matrix( 2, 2, [2, -1, -3, 2])
|
| 101 |
+
return m
|
| 102 |
+
|
| 103 |
+
def basis(self):
|
| 104 |
+
"""
|
| 105 |
+
Returns the number of independent generators of G_2
|
| 106 |
+
"""
|
| 107 |
+
return 14
|
| 108 |
+
|
| 109 |
+
def dynkin_diagram(self):
|
| 110 |
+
diag = "0≡<≡0\n1 2"
|
| 111 |
+
return diag
|
lib/python3.12/site-packages/sympy/liealgebras/weyl_group.py
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
from .cartan_type import CartanType
|
| 4 |
+
from mpmath import fac
|
| 5 |
+
from sympy.core.backend import Matrix, eye, Rational, igcd
|
| 6 |
+
from sympy.core.basic import Atom
|
| 7 |
+
|
| 8 |
+
class WeylGroup(Atom):
|
| 9 |
+
|
| 10 |
+
"""
|
| 11 |
+
For each semisimple Lie group, we have a Weyl group. It is a subgroup of
|
| 12 |
+
the isometry group of the root system. Specifically, it's the subgroup
|
| 13 |
+
that is generated by reflections through the hyperplanes orthogonal to
|
| 14 |
+
the roots. Therefore, Weyl groups are reflection groups, and so a Weyl
|
| 15 |
+
group is a finite Coxeter group.
|
| 16 |
+
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __new__(cls, cartantype):
|
| 20 |
+
obj = Atom.__new__(cls)
|
| 21 |
+
obj.cartan_type = CartanType(cartantype)
|
| 22 |
+
return obj
|
| 23 |
+
|
| 24 |
+
def generators(self):
|
| 25 |
+
"""
|
| 26 |
+
This method creates the generating reflections of the Weyl group for
|
| 27 |
+
a given Lie algebra. For a Lie algebra of rank n, there are n
|
| 28 |
+
different generating reflections. This function returns them as
|
| 29 |
+
a list.
|
| 30 |
+
|
| 31 |
+
Examples
|
| 32 |
+
========
|
| 33 |
+
|
| 34 |
+
>>> from sympy.liealgebras.weyl_group import WeylGroup
|
| 35 |
+
>>> c = WeylGroup("F4")
|
| 36 |
+
>>> c.generators()
|
| 37 |
+
['r1', 'r2', 'r3', 'r4']
|
| 38 |
+
"""
|
| 39 |
+
n = self.cartan_type.rank()
|
| 40 |
+
generators = []
|
| 41 |
+
for i in range(1, n+1):
|
| 42 |
+
reflection = "r"+str(i)
|
| 43 |
+
generators.append(reflection)
|
| 44 |
+
return generators
|
| 45 |
+
|
| 46 |
+
def group_order(self):
|
| 47 |
+
"""
|
| 48 |
+
This method returns the order of the Weyl group.
|
| 49 |
+
For types A, B, C, D, and E the order depends on
|
| 50 |
+
the rank of the Lie algebra. For types F and G,
|
| 51 |
+
the order is fixed.
|
| 52 |
+
|
| 53 |
+
Examples
|
| 54 |
+
========
|
| 55 |
+
|
| 56 |
+
>>> from sympy.liealgebras.weyl_group import WeylGroup
|
| 57 |
+
>>> c = WeylGroup("D4")
|
| 58 |
+
>>> c.group_order()
|
| 59 |
+
192.0
|
| 60 |
+
"""
|
| 61 |
+
n = self.cartan_type.rank()
|
| 62 |
+
if self.cartan_type.series == "A":
|
| 63 |
+
return fac(n+1)
|
| 64 |
+
|
| 65 |
+
if self.cartan_type.series in ("B", "C"):
|
| 66 |
+
return fac(n)*(2**n)
|
| 67 |
+
|
| 68 |
+
if self.cartan_type.series == "D":
|
| 69 |
+
return fac(n)*(2**(n-1))
|
| 70 |
+
|
| 71 |
+
if self.cartan_type.series == "E":
|
| 72 |
+
if n == 6:
|
| 73 |
+
return 51840
|
| 74 |
+
if n == 7:
|
| 75 |
+
return 2903040
|
| 76 |
+
if n == 8:
|
| 77 |
+
return 696729600
|
| 78 |
+
if self.cartan_type.series == "F":
|
| 79 |
+
return 1152
|
| 80 |
+
|
| 81 |
+
if self.cartan_type.series == "G":
|
| 82 |
+
return 12
|
| 83 |
+
|
| 84 |
+
def group_name(self):
|
| 85 |
+
"""
|
| 86 |
+
This method returns some general information about the Weyl group for
|
| 87 |
+
a given Lie algebra. It returns the name of the group and the elements
|
| 88 |
+
it acts on, if relevant.
|
| 89 |
+
"""
|
| 90 |
+
n = self.cartan_type.rank()
|
| 91 |
+
if self.cartan_type.series == "A":
|
| 92 |
+
return "S"+str(n+1) + ": the symmetric group acting on " + str(n+1) + " elements."
|
| 93 |
+
|
| 94 |
+
if self.cartan_type.series in ("B", "C"):
|
| 95 |
+
return "The hyperoctahedral group acting on " + str(2*n) + " elements."
|
| 96 |
+
|
| 97 |
+
if self.cartan_type.series == "D":
|
| 98 |
+
return "The symmetry group of the " + str(n) + "-dimensional demihypercube."
|
| 99 |
+
|
| 100 |
+
if self.cartan_type.series == "E":
|
| 101 |
+
if n == 6:
|
| 102 |
+
return "The symmetry group of the 6-polytope."
|
| 103 |
+
|
| 104 |
+
if n == 7:
|
| 105 |
+
return "The symmetry group of the 7-polytope."
|
| 106 |
+
|
| 107 |
+
if n == 8:
|
| 108 |
+
return "The symmetry group of the 8-polytope."
|
| 109 |
+
|
| 110 |
+
if self.cartan_type.series == "F":
|
| 111 |
+
return "The symmetry group of the 24-cell, or icositetrachoron."
|
| 112 |
+
|
| 113 |
+
if self.cartan_type.series == "G":
|
| 114 |
+
return "D6, the dihedral group of order 12, and symmetry group of the hexagon."
|
| 115 |
+
|
| 116 |
+
def element_order(self, weylelt):
|
| 117 |
+
"""
|
| 118 |
+
This method returns the order of a given Weyl group element, which should
|
| 119 |
+
be specified by the user in the form of products of the generating
|
| 120 |
+
reflections, i.e. of the form r1*r2 etc.
|
| 121 |
+
|
| 122 |
+
For types A-F, this method current works by taking the matrix form of
|
| 123 |
+
the specified element, and then finding what power of the matrix is the
|
| 124 |
+
identity. It then returns this power.
|
| 125 |
+
|
| 126 |
+
Examples
|
| 127 |
+
========
|
| 128 |
+
|
| 129 |
+
>>> from sympy.liealgebras.weyl_group import WeylGroup
|
| 130 |
+
>>> b = WeylGroup("B4")
|
| 131 |
+
>>> b.element_order('r1*r4*r2')
|
| 132 |
+
4
|
| 133 |
+
"""
|
| 134 |
+
n = self.cartan_type.rank()
|
| 135 |
+
if self.cartan_type.series == "A":
|
| 136 |
+
a = self.matrix_form(weylelt)
|
| 137 |
+
order = 1
|
| 138 |
+
while a != eye(n+1):
|
| 139 |
+
a *= self.matrix_form(weylelt)
|
| 140 |
+
order += 1
|
| 141 |
+
return order
|
| 142 |
+
|
| 143 |
+
if self.cartan_type.series == "D":
|
| 144 |
+
a = self.matrix_form(weylelt)
|
| 145 |
+
order = 1
|
| 146 |
+
while a != eye(n):
|
| 147 |
+
a *= self.matrix_form(weylelt)
|
| 148 |
+
order += 1
|
| 149 |
+
return order
|
| 150 |
+
|
| 151 |
+
if self.cartan_type.series == "E":
|
| 152 |
+
a = self.matrix_form(weylelt)
|
| 153 |
+
order = 1
|
| 154 |
+
while a != eye(8):
|
| 155 |
+
a *= self.matrix_form(weylelt)
|
| 156 |
+
order += 1
|
| 157 |
+
return order
|
| 158 |
+
|
| 159 |
+
if self.cartan_type.series == "G":
|
| 160 |
+
elts = list(weylelt)
|
| 161 |
+
reflections = elts[1::3]
|
| 162 |
+
m = self.delete_doubles(reflections)
|
| 163 |
+
while self.delete_doubles(m) != m:
|
| 164 |
+
m = self.delete_doubles(m)
|
| 165 |
+
reflections = m
|
| 166 |
+
if len(reflections) % 2 == 1:
|
| 167 |
+
return 2
|
| 168 |
+
|
| 169 |
+
elif len(reflections) == 0:
|
| 170 |
+
return 1
|
| 171 |
+
|
| 172 |
+
else:
|
| 173 |
+
if len(reflections) == 1:
|
| 174 |
+
return 2
|
| 175 |
+
else:
|
| 176 |
+
m = len(reflections) // 2
|
| 177 |
+
lcm = (6 * m)/ igcd(m, 6)
|
| 178 |
+
order = lcm / m
|
| 179 |
+
return order
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
if self.cartan_type.series == 'F':
|
| 183 |
+
a = self.matrix_form(weylelt)
|
| 184 |
+
order = 1
|
| 185 |
+
while a != eye(4):
|
| 186 |
+
a *= self.matrix_form(weylelt)
|
| 187 |
+
order += 1
|
| 188 |
+
return order
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
if self.cartan_type.series in ("B", "C"):
|
| 192 |
+
a = self.matrix_form(weylelt)
|
| 193 |
+
order = 1
|
| 194 |
+
while a != eye(n):
|
| 195 |
+
a *= self.matrix_form(weylelt)
|
| 196 |
+
order += 1
|
| 197 |
+
return order
|
| 198 |
+
|
| 199 |
+
def delete_doubles(self, reflections):
|
| 200 |
+
"""
|
| 201 |
+
This is a helper method for determining the order of an element in the
|
| 202 |
+
Weyl group of G2. It takes a Weyl element and if repeated simple reflections
|
| 203 |
+
in it, it deletes them.
|
| 204 |
+
"""
|
| 205 |
+
counter = 0
|
| 206 |
+
copy = list(reflections)
|
| 207 |
+
for elt in copy:
|
| 208 |
+
if counter < len(copy)-1:
|
| 209 |
+
if copy[counter + 1] == elt:
|
| 210 |
+
del copy[counter]
|
| 211 |
+
del copy[counter]
|
| 212 |
+
counter += 1
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
return copy
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def matrix_form(self, weylelt):
|
| 219 |
+
"""
|
| 220 |
+
This method takes input from the user in the form of products of the
|
| 221 |
+
generating reflections, and returns the matrix corresponding to the
|
| 222 |
+
element of the Weyl group. Since each element of the Weyl group is
|
| 223 |
+
a reflection of some type, there is a corresponding matrix representation.
|
| 224 |
+
This method uses the standard representation for all the generating
|
| 225 |
+
reflections.
|
| 226 |
+
|
| 227 |
+
Examples
|
| 228 |
+
========
|
| 229 |
+
|
| 230 |
+
>>> from sympy.liealgebras.weyl_group import WeylGroup
|
| 231 |
+
>>> f = WeylGroup("F4")
|
| 232 |
+
>>> f.matrix_form('r2*r3')
|
| 233 |
+
Matrix([
|
| 234 |
+
[1, 0, 0, 0],
|
| 235 |
+
[0, 1, 0, 0],
|
| 236 |
+
[0, 0, 0, -1],
|
| 237 |
+
[0, 0, 1, 0]])
|
| 238 |
+
|
| 239 |
+
"""
|
| 240 |
+
elts = list(weylelt)
|
| 241 |
+
reflections = elts[1::3]
|
| 242 |
+
n = self.cartan_type.rank()
|
| 243 |
+
if self.cartan_type.series == 'A':
|
| 244 |
+
matrixform = eye(n+1)
|
| 245 |
+
for elt in reflections:
|
| 246 |
+
a = int(elt)
|
| 247 |
+
mat = eye(n+1)
|
| 248 |
+
mat[a-1, a-1] = 0
|
| 249 |
+
mat[a-1, a] = 1
|
| 250 |
+
mat[a, a-1] = 1
|
| 251 |
+
mat[a, a] = 0
|
| 252 |
+
matrixform *= mat
|
| 253 |
+
return matrixform
|
| 254 |
+
|
| 255 |
+
if self.cartan_type.series == 'D':
|
| 256 |
+
matrixform = eye(n)
|
| 257 |
+
for elt in reflections:
|
| 258 |
+
a = int(elt)
|
| 259 |
+
mat = eye(n)
|
| 260 |
+
if a < n:
|
| 261 |
+
mat[a-1, a-1] = 0
|
| 262 |
+
mat[a-1, a] = 1
|
| 263 |
+
mat[a, a-1] = 1
|
| 264 |
+
mat[a, a] = 0
|
| 265 |
+
matrixform *= mat
|
| 266 |
+
else:
|
| 267 |
+
mat[n-2, n-1] = -1
|
| 268 |
+
mat[n-2, n-2] = 0
|
| 269 |
+
mat[n-1, n-2] = -1
|
| 270 |
+
mat[n-1, n-1] = 0
|
| 271 |
+
matrixform *= mat
|
| 272 |
+
return matrixform
|
| 273 |
+
|
| 274 |
+
if self.cartan_type.series == 'G':
|
| 275 |
+
matrixform = eye(3)
|
| 276 |
+
for elt in reflections:
|
| 277 |
+
a = int(elt)
|
| 278 |
+
if a == 1:
|
| 279 |
+
gen1 = Matrix([[1, 0, 0], [0, 0, 1], [0, 1, 0]])
|
| 280 |
+
matrixform *= gen1
|
| 281 |
+
else:
|
| 282 |
+
gen2 = Matrix([[Rational(2, 3), Rational(2, 3), Rational(-1, 3)],
|
| 283 |
+
[Rational(2, 3), Rational(-1, 3), Rational(2, 3)],
|
| 284 |
+
[Rational(-1, 3), Rational(2, 3), Rational(2, 3)]])
|
| 285 |
+
matrixform *= gen2
|
| 286 |
+
return matrixform
|
| 287 |
+
|
| 288 |
+
if self.cartan_type.series == 'F':
|
| 289 |
+
matrixform = eye(4)
|
| 290 |
+
for elt in reflections:
|
| 291 |
+
a = int(elt)
|
| 292 |
+
if a == 1:
|
| 293 |
+
mat = Matrix([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
|
| 294 |
+
matrixform *= mat
|
| 295 |
+
elif a == 2:
|
| 296 |
+
mat = Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
|
| 297 |
+
matrixform *= mat
|
| 298 |
+
elif a == 3:
|
| 299 |
+
mat = Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]])
|
| 300 |
+
matrixform *= mat
|
| 301 |
+
else:
|
| 302 |
+
|
| 303 |
+
mat = Matrix([[Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2)],
|
| 304 |
+
[Rational(1, 2), Rational(1, 2), Rational(-1, 2), Rational(-1, 2)],
|
| 305 |
+
[Rational(1, 2), Rational(-1, 2), Rational(1, 2), Rational(-1, 2)],
|
| 306 |
+
[Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(1, 2)]])
|
| 307 |
+
matrixform *= mat
|
| 308 |
+
return matrixform
|
| 309 |
+
|
| 310 |
+
if self.cartan_type.series == 'E':
|
| 311 |
+
matrixform = eye(8)
|
| 312 |
+
for elt in reflections:
|
| 313 |
+
a = int(elt)
|
| 314 |
+
if a == 1:
|
| 315 |
+
mat = Matrix([[Rational(3, 4), Rational(1, 4), Rational(1, 4), Rational(1, 4),
|
| 316 |
+
Rational(1, 4), Rational(1, 4), Rational(1, 4), Rational(-1, 4)],
|
| 317 |
+
[Rational(1, 4), Rational(3, 4), Rational(-1, 4), Rational(-1, 4),
|
| 318 |
+
Rational(-1, 4), Rational(-1, 4), Rational(1, 4), Rational(-1, 4)],
|
| 319 |
+
[Rational(1, 4), Rational(-1, 4), Rational(3, 4), Rational(-1, 4),
|
| 320 |
+
Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(1, 4)],
|
| 321 |
+
[Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(3, 4),
|
| 322 |
+
Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(1, 4)],
|
| 323 |
+
[Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4),
|
| 324 |
+
Rational(3, 4), Rational(-1, 4), Rational(-1, 4), Rational(1, 4)],
|
| 325 |
+
[Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4),
|
| 326 |
+
Rational(-1, 4), Rational(3, 4), Rational(-1, 4), Rational(1, 4)],
|
| 327 |
+
[Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4),
|
| 328 |
+
Rational(-1, 4), Rational(-1, 4), Rational(-3, 4), Rational(1, 4)],
|
| 329 |
+
[Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4),
|
| 330 |
+
Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(3, 4)]])
|
| 331 |
+
matrixform *= mat
|
| 332 |
+
elif a == 2:
|
| 333 |
+
mat = eye(8)
|
| 334 |
+
mat[0, 0] = 0
|
| 335 |
+
mat[0, 1] = -1
|
| 336 |
+
mat[1, 0] = -1
|
| 337 |
+
mat[1, 1] = 0
|
| 338 |
+
matrixform *= mat
|
| 339 |
+
else:
|
| 340 |
+
mat = eye(8)
|
| 341 |
+
mat[a-3, a-3] = 0
|
| 342 |
+
mat[a-3, a-2] = 1
|
| 343 |
+
mat[a-2, a-3] = 1
|
| 344 |
+
mat[a-2, a-2] = 0
|
| 345 |
+
matrixform *= mat
|
| 346 |
+
return matrixform
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
if self.cartan_type.series in ("B", "C"):
|
| 350 |
+
matrixform = eye(n)
|
| 351 |
+
for elt in reflections:
|
| 352 |
+
a = int(elt)
|
| 353 |
+
mat = eye(n)
|
| 354 |
+
if a == 1:
|
| 355 |
+
mat[0, 0] = -1
|
| 356 |
+
matrixform *= mat
|
| 357 |
+
else:
|
| 358 |
+
mat[a - 2, a - 2] = 0
|
| 359 |
+
mat[a-2, a-1] = 1
|
| 360 |
+
mat[a - 1, a - 2] = 1
|
| 361 |
+
mat[a -1, a - 1] = 0
|
| 362 |
+
matrixform *= mat
|
| 363 |
+
return matrixform
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def coxeter_diagram(self):
|
| 368 |
+
"""
|
| 369 |
+
This method returns the Coxeter diagram corresponding to a Weyl group.
|
| 370 |
+
The Coxeter diagram can be obtained from a Lie algebra's Dynkin diagram
|
| 371 |
+
by deleting all arrows; the Coxeter diagram is the undirected graph.
|
| 372 |
+
The vertices of the Coxeter diagram represent the generating reflections
|
| 373 |
+
of the Weyl group, $s_i$. An edge is drawn between $s_i$ and $s_j$ if the order
|
| 374 |
+
$m(i, j)$ of $s_is_j$ is greater than two. If there is one edge, the order
|
| 375 |
+
$m(i, j)$ is 3. If there are two edges, the order $m(i, j)$ is 4, and if there
|
| 376 |
+
are three edges, the order $m(i, j)$ is 6.
|
| 377 |
+
|
| 378 |
+
Examples
|
| 379 |
+
========
|
| 380 |
+
|
| 381 |
+
>>> from sympy.liealgebras.weyl_group import WeylGroup
|
| 382 |
+
>>> c = WeylGroup("B3")
|
| 383 |
+
>>> print(c.coxeter_diagram())
|
| 384 |
+
0---0===0
|
| 385 |
+
1 2 3
|
| 386 |
+
"""
|
| 387 |
+
n = self.cartan_type.rank()
|
| 388 |
+
if self.cartan_type.series in ("A", "D", "E"):
|
| 389 |
+
return self.cartan_type.dynkin_diagram()
|
| 390 |
+
|
| 391 |
+
if self.cartan_type.series in ("B", "C"):
|
| 392 |
+
diag = "---".join("0" for i in range(1, n)) + "===0\n"
|
| 393 |
+
diag += " ".join(str(i) for i in range(1, n+1))
|
| 394 |
+
return diag
|
| 395 |
+
|
| 396 |
+
if self.cartan_type.series == "F":
|
| 397 |
+
diag = "0---0===0---0\n"
|
| 398 |
+
diag += " ".join(str(i) for i in range(1, 5))
|
| 399 |
+
return diag
|
| 400 |
+
|
| 401 |
+
if self.cartan_type.series == "G":
|
| 402 |
+
diag = "0≡≡≡0\n1 2"
|
| 403 |
+
return diag
|
lib/python3.12/site-packages/sympy/simplify/__pycache__/cse_main.cpython-312.pyc
ADDED
|
Binary file (35.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/simplify/__pycache__/epathtools.cpython-312.pyc
ADDED
|
Binary file (11.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/simplify/__pycache__/fu.cpython-312.pyc
ADDED
|
Binary file (81.4 kB). View file
|
|
|