Implemented splitting of algebras
This commit is contained in:
parent
9c6e8270e4
commit
2809d9c8c5
|
@ -0,0 +1,555 @@
|
|||
r"""
|
||||
Implementations of algorithms for associative algebras. Many of those
|
||||
appear in the reference of Sage but the class is somewhat buggy for
|
||||
the moment.
|
||||
|
||||
A lot of it is very hacky but I just want to get something that works
|
||||
until the Sage implementation can be fixed.
|
||||
|
||||
It's also pretty unoptimized, there is a lot of room for improvement
|
||||
if it is too slow.
|
||||
"""
|
||||
###########################################################################
|
||||
# Copyright (C) 2024 Mickaël Montessinos (mickael.montessinos@mif.vu.lt),#
|
||||
# #
|
||||
# Distributed under the terms of the GNU General Public License (GPL) #
|
||||
# either version 3, or (at your option) any later version #
|
||||
# #
|
||||
# http://www.gnu.org/licenses/ #
|
||||
###########################################################################
|
||||
import itertools
|
||||
from sage.misc.cachefunc import cached_function
|
||||
from sage.categories.algebras import Algebras
|
||||
from sage.misc.misc_c import prod
|
||||
from sage.algebras.all import FiniteDimensionalAlgebra
|
||||
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
||||
from sage.arith.misc import CRT_list
|
||||
from sage.matrix.matrix_space import MatrixSpace
|
||||
from sage.matrix.constructor import matrix
|
||||
from sage.matrix.special import block_matrix
|
||||
from sage.modules.free_module_element import vector
|
||||
|
||||
|
||||
def subalgebra(A, basis, category=None):
|
||||
r"""
|
||||
The main issue with the algebra class in Sage is that the submodule method
|
||||
used to generate subalgebras raises an error. We implement this function
|
||||
instead which generates structure constants for a subalgebra.
|
||||
|
||||
This is not very efficient since we have to recompute the multiplication
|
||||
table instead of using multiplication in A, but it works
|
||||
|
||||
An advantage is that the identity of the subalgebra needs not be the
|
||||
identity of A, so we may represent two-sided ideals as algebras as well.
|
||||
|
||||
INPUT:
|
||||
|
||||
-``A`` -- A finite dimensional algebra with basis
|
||||
|
||||
-``basis`` -- A free family in A generating a subalgebra
|
||||
|
||||
OUTPUT:
|
||||
|
||||
-``S`` -- The subalgebra of ``A`` generated by basis
|
||||
|
||||
-``to_S`` -- A left inverse to ``from_S`` (raises an error if the element
|
||||
is not in S)
|
||||
|
||||
-``from_S`` -- The inclusion map from S to A
|
||||
"""
|
||||
k = A.base_ring()
|
||||
mat = matrix([e.vector() for e in basis]).transpose()
|
||||
tables = [matrix([mat.solve_right((f*e).vector()) for f in basis])
|
||||
for e in basis]
|
||||
if category is None:
|
||||
category = A.category()
|
||||
S = FiniteDimensionalAlgebra(k, tables,
|
||||
assume_associative = True,
|
||||
category = category)
|
||||
to_S = lambda a : S(mat.solve_right(a.vector()))
|
||||
from_S = lambda s : A(mat * s.vector())
|
||||
return S, to_S, from_S
|
||||
|
||||
|
||||
def in_column_space(mat, vec):
|
||||
r"""
|
||||
Checks if vec is in the space spanned by the columns of mat.
|
||||
|
||||
Not very pretty but does the job
|
||||
|
||||
EXAMPLES ::
|
||||
|
||||
sage: from vector_bundle.algebras import in_column_space
|
||||
sage: mat = matrix(QQ,2,1,[1,2])
|
||||
sage: in_column_space(mat, vector([2,4]))
|
||||
True
|
||||
sage: in_column_space(mat, vector([0,1]))
|
||||
False
|
||||
"""
|
||||
try:
|
||||
mat.solve_right(vec)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def clear_zero_columns(mat):
|
||||
return mat.matrix_from_columns([i for i in range(mat.ncols())
|
||||
if not mat[:,i].is_zero()])
|
||||
|
||||
|
||||
def subalgebra_from_gens(A, gens, category=None):
|
||||
mat = matrix([gen.vector() for gen in gens]).echelon_form()
|
||||
basis = [A(row) for row in mat.rows() if not row.is_zero()]
|
||||
return subalgebra(A, basis, category=category)
|
||||
|
||||
|
||||
@cached_function
|
||||
def radical(A):
|
||||
return subalgebra(A, A.radical_basis())
|
||||
|
||||
|
||||
@cached_function
|
||||
def center(A):
|
||||
return subalgebra(A, A.center_basis(), category=A.category().Commutative())
|
||||
|
||||
|
||||
def wedderburn_malcev_basis(A):
|
||||
r"""
|
||||
Compute a basis of A of the form ``[r1,r2,...,rk,s1,...,sm]`` such that
|
||||
``[r1,...,rk]`` is a basis of the radical and ``[s1,...,sm]`` is a basis
|
||||
of a semisimple subalgebra of ``A``.
|
||||
|
||||
EXAMPLES ::
|
||||
|
||||
sage: from vector_bundle import VectorBundle
|
||||
sage: from vector_bundle.algebras import wedderburn_malcev_basis
|
||||
sage: F.<x> = FunctionField(GF(7))
|
||||
sage: L1 = VectorBundle(F, 3 * x.zeros()[0].divisor())
|
||||
sage: L2 = VectorBundle(F, -2 * x.poles()[0].divisor())
|
||||
sage: V = L1.direct_sum(L2)
|
||||
sage: T = matrix(F,2,2,[5*x^2 + 3*x + 2, x^2 + 4*x + 4, 4*x^2 + x + 1, 6*x^2 + 4*x + 5])
|
||||
sage: V = V.apply_isomorphism(T)
|
||||
sage: End = V.end()
|
||||
sage: A, to_A, from_A = End.global_algebra()
|
||||
sage: basis = wedderburn_malcev_basis(A)
|
||||
sage: [T^-1 * from_A(b) * T for b in basis]
|
||||
[
|
||||
[0 0] [1 0]
|
||||
[0 1], [0 0]
|
||||
]
|
||||
"""
|
||||
k = A.base_ring()
|
||||
n = A.dimension()
|
||||
basis = A.basis()
|
||||
#Compute bases of the powers of the radical.
|
||||
gens = [A.radical_basis()]
|
||||
while gens[-1]:
|
||||
gens.append([a*b for a in gens[0]
|
||||
for b in gens[-1] if not (a*b).is_zero()])
|
||||
gens = gens[:-1]
|
||||
gen_vecs = [[g.vector() for g in gen] for gen in gens]
|
||||
#Organise them into a hierchical basis
|
||||
mat = clear_zero_columns(matrix(gen_vecs[-1]).echelon_form().transpose())
|
||||
rad_power_steps = [n, n - mat.ncols()]
|
||||
for i in range(len(gens)-2, -1, -1):
|
||||
new_cols = matrix([c for c in gen_vecs[i]
|
||||
if not in_column_space(mat, c)])
|
||||
new_cols = clear_zero_columns(new_cols.echelon_form().transpose())
|
||||
mat = block_matrix([[new_cols, mat]])
|
||||
rad_power_steps.append(n - mat.ncols())
|
||||
rad_power_steps.append(0)
|
||||
rad_power_steps.reverse()
|
||||
#Now, add a complement which we'll turn into an algebra
|
||||
for i in range(n):
|
||||
col = matrix(k,n,1,[1 if j == i else 0 for j in range(n)])
|
||||
if not in_column_space(mat, col):
|
||||
mat = block_matrix([[col, mat]])
|
||||
if mat.ncols() == n:
|
||||
break
|
||||
r = rad_power_steps[1]
|
||||
imat = mat**-1
|
||||
#We may now begin running the algorithm in earnest.
|
||||
basis = [A(c) for c in mat.columns()]
|
||||
table = [imat * c.left_matrix() * mat for c in basis[:r]]
|
||||
for first, stop in zip(rad_power_steps[:-1], rad_power_steps[1:]):
|
||||
b_pairs = list(itertools.product(enumerate(basis[:r]), repeat=2))
|
||||
d_tuples = [[0]*i + [d] + [0]*(r-1-i)
|
||||
for i in range(r) for d in basis[first:stop]]
|
||||
#(first, stop) marks the positions of the vectors generating V_i.
|
||||
eq = matrix([sum([list(mat[:,first:stop].solve_right(
|
||||
(bi*ds[j]
|
||||
+ ds[i]*bj
|
||||
- sum([table[i][j,s]*d for (s, d) in enumerate(ds)])).vector()))
|
||||
for (i, bi),(j, bj) in b_pairs], [])
|
||||
for ds in d_tuples])
|
||||
target = vector(sum([list(mat[:,first:stop].solve_right(
|
||||
(sum([table[i][j,s]*b for s, b in enumerate(basis[:r])])
|
||||
- bi*bj).vector()))
|
||||
for (i, bi), (j, bj) in b_pairs], []))
|
||||
sol = eq.solve_left(target)
|
||||
sol_chunks = [sol[i: i + stop - first]
|
||||
for i in range(0, len(sol), stop - first)]
|
||||
d_sols = [A(mat[:, first: stop] * d) for d in sol_chunks]
|
||||
for (i, d) in enumerate(d_sols):
|
||||
basis[i] += d
|
||||
return basis[:r]
|
||||
|
||||
|
||||
@cached_function
|
||||
def wedderburn_malcev_complement(A):
|
||||
category = A.category().Semisimple()
|
||||
return subalgebra(A, wedderburn_malcev_basis(A), category=category)
|
||||
|
||||
|
||||
def idems_from_element(e):
|
||||
factorization = e.minimal_polynomial().factor()
|
||||
factors = [fa[0] for fa in factorization]
|
||||
n = len(factors)
|
||||
hs = [CRT_list([0]*i + [1] + [0]*(n-i-1), factors) for i in range(n)]
|
||||
return [h(e) for h in hs]
|
||||
|
||||
|
||||
def splitting_from_idems(C, idems):
|
||||
return [subalgebra_from_gens(
|
||||
C,
|
||||
[idem*c*idem for c in C.basis()])
|
||||
for idem in idems]
|
||||
|
||||
|
||||
def wedderburn_splitting_large_field(C):
|
||||
r"""
|
||||
Assume that C is semi-simple and commutative, and that the base field of C
|
||||
is finite but larger that 2*C.dimension()
|
||||
"""
|
||||
k = C.base_ring()
|
||||
d = C.dimension()
|
||||
search_set = [k(i) for i in range(2*C.dimension())]
|
||||
while True:
|
||||
a = C(vector([k.random_element() for _ in range(d)]))
|
||||
f = a.minimal_polynomial()
|
||||
if f.degree() == d:
|
||||
break
|
||||
return splitting_from_idems(C, idems_from_element(a))
|
||||
|
||||
|
||||
def wedderburn_splitting_small_field(C):
|
||||
r"""
|
||||
Assume that C is semi-simple and commutative, and that the base field of C
|
||||
is finite.
|
||||
"""
|
||||
k = C.base_ring()
|
||||
test = matrix([(a**k.cardinality()- a).vector() for a in C.basis()])
|
||||
basis = frob.left_kernel()
|
||||
if len(basis) == 1:
|
||||
return C, lambda a: a, lambda a: a
|
||||
one = C.one()
|
||||
e = basis[0]
|
||||
mat = matrix([one.vector(), e.vector()])
|
||||
if mat.rank() == 1:
|
||||
e = basis[1]
|
||||
partial_split = splitting_from_idems(C,idems_from_element(e))
|
||||
splits_of_splits = [wedderburn_splitting_comm(S[0]) for S in partial_split]
|
||||
return sum([[(split[0],
|
||||
lambda a: split[1](S[1](a)),
|
||||
lambda a: S[1](split[1](a)))
|
||||
for split in splits]
|
||||
for splits, S in zip(partial_split, splits_of_splits)], [])
|
||||
|
||||
|
||||
def wedderburn_splitting_comm(C):
|
||||
r"""
|
||||
Assume that C is semi-simple commutative. Return an isomorphism from A to a direct
|
||||
sum of simple algebras.
|
||||
"""
|
||||
n = C.dimension()
|
||||
if 2*n < C.base_ring().cardinality():
|
||||
return wedderburn_splitting_large_field(C)
|
||||
return wedderburn_splitting_small_field(C)
|
||||
|
||||
|
||||
def wedderburn_splitting(A):
|
||||
r"""
|
||||
Assume that A is semi-simple. Return a list of simple algebras with maps
|
||||
to and from A. A is isomorphic to the direct sum of these algebras.
|
||||
|
||||
EXAMPLES ::
|
||||
|
||||
sage: from vector_bundle import VectorBundle
|
||||
sage: from vector_bundle.algebras import (wedderburn_malcev_complement,
|
||||
....: wedderburn_splitting)
|
||||
sage: F.<x> = FunctionField(GF(7))
|
||||
sage: L1 = VectorBundle(F, 3 * x.zeros()[0].divisor())
|
||||
sage: L2 = VectorBundle(F, -2 * x.poles()[0].divisor())
|
||||
sage: V = L1.direct_sum(L2)
|
||||
sage: T = matrix(F,2,2,[5*x^2 + 3*x + 2,
|
||||
....: x^2 + 4*x + 4, 4*x^2 + x + 1,
|
||||
....: 6*x^2 + 4*x + 5])
|
||||
sage: V = V.apply_isomorphism(T)
|
||||
sage: End = V.end()
|
||||
sage: A, to_A, from_A = End.global_algebra()
|
||||
sage: S, to_S, from_S = wedderburn_malcev_complement(A)
|
||||
sage: simples = wedderburn_splitting(S)
|
||||
sage: idems = [T^-1 * from_A(from_S(s[2](s[0].one()))) * T for s in simples]
|
||||
sage: len(idems)
|
||||
2
|
||||
sage: matrix(GF(7),[[1, 0], [0, 0]]) in idems
|
||||
True
|
||||
sage: matrix(GF(7),[[0, 0], [0, 1]]) in idems
|
||||
True
|
||||
"""
|
||||
C, to_C, from_C = center(A)
|
||||
split = wedderburn_splitting_comm(C)
|
||||
return [subalgebra_from_gens(A, [a * from_C(s[2](b))
|
||||
for a in A.basis() for b in s[0].basis()])
|
||||
for s in split]
|
||||
|
||||
|
||||
def subfield_as_field(A, basis):
|
||||
r"""
|
||||
Return a bijection from the center of A to the corresponding extension
|
||||
of the base field of A
|
||||
"""
|
||||
n = len(basis)
|
||||
if n == 1:
|
||||
to_k = lambda a : matrix([A.one().vector()]).solve_left(a.vector())[0]
|
||||
from_k = lambda a : a*A.one()
|
||||
return A.base_ring(), to_k, from_k
|
||||
k = A.base_ring()
|
||||
q = k.cardinality()
|
||||
mat = matrix([(c**(q**(n-1)) - c).vector() for c in basis])
|
||||
ker = mat.left_kernel().tranpose()
|
||||
b = [c for c in basis() if not in_column_space(ker, c)][0]
|
||||
f = b.minimal_polynomial()
|
||||
K = k.extension(f)
|
||||
mat = matrix([(b**n).vector() for n in range(f.degree() - 1)])
|
||||
def to_K(a):
|
||||
sol = mat.solve_right(a.vector())
|
||||
return K(sol)
|
||||
from_K = lambda a: A(mat*vector(a.list()))
|
||||
return K, to_K, from_K
|
||||
|
||||
|
||||
def min_poly_over_subfield(A, basis, e):
|
||||
K, to_K, from_K = subfield_as_field(A, basis)
|
||||
mat = matrix([c.vector() for c in basis]).transpose()
|
||||
n = 0
|
||||
acc = e
|
||||
while(True):
|
||||
try:
|
||||
sol = mat.solve_right(acc.vector())
|
||||
break
|
||||
except ValueError:
|
||||
n +=1
|
||||
mat = block_matrix([[
|
||||
mat,
|
||||
matrix([(c*acc).vector() for c in basis]).transpose()]])
|
||||
acc *= e
|
||||
d = len(basis)
|
||||
coeffs = [-to_K(sum([c*v for c, v in zip(sol[i:i+d], basis)]))
|
||||
for i in range(0, len(sol), d)] + [1]
|
||||
return PolynomialRing(K, 't')(coeffs), mat
|
||||
|
||||
|
||||
def zero_div_from_min_poly(a, f):
|
||||
facto = f.factor()
|
||||
if len(facto) > 1:
|
||||
return((facto[0][0]**facto[0][1])(a),
|
||||
prod([(fa[0]**fa[1])(a) for fa in facto[1:]]))
|
||||
if facto[0][1] > 1:
|
||||
return((facto[0][0])(a), (facto[0]**(facto[0][1]-1))(a))
|
||||
return None
|
||||
|
||||
|
||||
def solve_norm_equation(K, L, a):
|
||||
r"""
|
||||
This is not a general norm equation solver. It works only in the cases
|
||||
relevant for zero_div!
|
||||
"""
|
||||
d, r = L.degree().quorem(K.degree())
|
||||
assert r == 0
|
||||
if d % 2 == 1:
|
||||
g = UnivariatePolynomialRing(L)([-a] + [0]*(d-1) + [1])
|
||||
facto = g.factor()
|
||||
assert all([f[0].degree() == 1 for f in facto])
|
||||
h = facto[0][0]
|
||||
return -h.coefficient(0)/h.coefficient(1)
|
||||
if d == 2:
|
||||
b = sqrt(L(-a))
|
||||
if b not in K:
|
||||
return b
|
||||
q = K.cardinality()
|
||||
f = UnivariatePolynomialRing([1] + [0]*1 + [1])
|
||||
a = f.roots()[0][0]
|
||||
return a*b
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def zero_div(A):
|
||||
r""" Return a pair of zero divisors in A or conclude that A is a field
|
||||
extension of its base field (which, as always, is assumed finite)
|
||||
"""
|
||||
rad = A.radical_basis()
|
||||
if rad:
|
||||
a = rad[0]
|
||||
n = [a**n for n,_ in enumerate(rad)].index(0)
|
||||
return a, a**(n-1)
|
||||
#A is semisimple
|
||||
split = wedderburn_splitting(A)
|
||||
if len(split) > 1:
|
||||
s0, to_s0, from_s0 = split[0]
|
||||
s1, to_s1, from_s1 = split[1]
|
||||
return (from_s0(s0.one()), from_s1(s1.one()))
|
||||
#A is simple
|
||||
center_mat = matrix([c.vector() for c in A.center_basis()]).transpose()
|
||||
q = A.base_ring().cardinality()**center_mat.ncols()
|
||||
if A.is_commutative():
|
||||
return None, None
|
||||
#A is not a field
|
||||
b = [a for a in A.basis() if not in_column_space(center_mat, a.vector())][0]
|
||||
f, Cb_mat = min_poly_over_subfield(A, A.center_basis(), b)
|
||||
t = zero_div_from_min_poly(b, f)
|
||||
if t is not None:
|
||||
return t
|
||||
#C(b) is a field
|
||||
if f.degree() % 2 == 0 and f.degree() > 2:
|
||||
Cb_basis = [A(c) for c in Cb_mat.columns()]
|
||||
eq = matrix([(a**(q**2) - a).vector() for a in Cb_basis])
|
||||
ker = eq.left_kernel()
|
||||
b = [sum([v[i]*b for i, b in enumerate(Cb_basis)]) for v in ker
|
||||
if any(v[q:])]
|
||||
#C(b) has rank odd or 2 over C
|
||||
eq = matrix([(b*c - c*b**q).vector() for c in A.basis()])
|
||||
c = A(eq.left_kernel()[0])
|
||||
fc, _ = min_poly_over_subfield(A, A.center_basis(), c)
|
||||
t = zero_div_from_min_poly(c, f)
|
||||
if t is not None:
|
||||
return t
|
||||
#c is invertible and c^-1bc = b^q
|
||||
fb, mat_Ab = min_poly_over_subfield(A, A.center_basis(), b)
|
||||
basis_Ab = [A(v) for v in mat_Ab.columns()]
|
||||
_, mat_Abc = min_poly_over_subfield(A, basis_Ab, c)
|
||||
if mat_Abc.ncols() < A.dimension():
|
||||
basis = [A(v) for v in mat_Abc.columns()]
|
||||
S, to_S, from_S = subalgebra(A, basis)
|
||||
z1, z2 = zero_divisor(S)
|
||||
return from_S(z1), from_S(z2)
|
||||
#The subalgebra generated by the center, b and c is equal to A
|
||||
n = fc.degree()
|
||||
assert all([fc.coefficient(i) == 0 for i in range(1,n)])
|
||||
alpha = -fc.coefficient(0)
|
||||
K, to_K, from_K = subfield_as_field(A, A.center_basis())
|
||||
L, to_L, from_L = subfield_as_field(A, basis_Ab)
|
||||
d = from_L(solve_norm_equation(K, L, 1/alpha))
|
||||
x = 1 - c*d
|
||||
y = sum([c**i * d**sum([q**j for j in range(i)]) for i in range(n)])
|
||||
return x, y
|
||||
|
||||
|
||||
def idempotent_from_zero_div(A, z):
|
||||
if z is None:
|
||||
return A.one(), 1
|
||||
mat = matrix([(a*z).vector() for a in A.basis()]).echelon_form()
|
||||
ideal_basis = [A(r) for r in mat.rows() if not r.is_zero()]
|
||||
eq = matrix([sum([list((a*e).vector()) for a in ideal_basis], [])
|
||||
for e in ideal_basis])
|
||||
target = vector(sum([list(a.vector()) for a in ideal_basis], []))
|
||||
e = sum([c*v for c, v in zip(eq.solve_left(target), ideal_basis)])
|
||||
n = (A.dimension() // len(A.center_basis())).sqrt()
|
||||
r = len(ideal_basis) // (len(A.center_basis())*n)
|
||||
if r > n/2:
|
||||
return 1 - e, n - r
|
||||
return e, r
|
||||
|
||||
|
||||
def rank_one_idempotent(A):
|
||||
r = len(A.center_basis())
|
||||
n = (A.dimension() // r).sqrt()
|
||||
z1, _ = zero_div(A)
|
||||
e, d = idempotent_from_zero_div(A, z1)
|
||||
if d == 1:
|
||||
return e
|
||||
else:
|
||||
S, to_S, from_S = subalgebra_from_gens(
|
||||
A,
|
||||
[a*e*b for a in A.basis() for b in A.basis()])
|
||||
e = rank_one_idempotent(S)
|
||||
return from_S(e)
|
||||
|
||||
|
||||
class SplitAlgebra:
|
||||
r"""
|
||||
Class for a splitting of an algebra. Contains the isomorphic matrix algebra
|
||||
and maps to and from it
|
||||
"""
|
||||
def __init__(self, A, to_A=None, from_A = None):
|
||||
z = rank_one_idempotent(A)
|
||||
self.A = A
|
||||
self._to_A = to_A
|
||||
self._from_A = from_A
|
||||
ideal_gens = [a*z for a in A.basis()]
|
||||
center_basis = A.center_basis()
|
||||
self._r = len(center_basis)
|
||||
basis_over_center = []
|
||||
mat = matrix(A.base_ring(), A.dimension(), 0, [])
|
||||
for g in ideal_gens:
|
||||
if not in_column_space(mat, g.vector()):
|
||||
basis_over_center.append(g)
|
||||
mat = block_matrix([[
|
||||
mat,
|
||||
matrix([(f*g).vector() for f in center_basis]).transpose()]])
|
||||
self._K, self._to_K, self._from_K = subfield_as_field(A, center_basis)
|
||||
self._n = len(basis_over_center)
|
||||
self.M = MatrixSpace(self._K, self._n)
|
||||
self._eq = None
|
||||
self._ideal_matrix = mat
|
||||
self._basis_over_center = basis_over_center
|
||||
|
||||
def _coords_over_center(self, a):
|
||||
sol = self._ideal_matrix.solve_right(a.vector())
|
||||
coefs = [sum([c*v for c, v in zip(sol[i: i+self._r], self.A.center_basis())])
|
||||
for i in range(0, self._n, self._r)]
|
||||
return vector(self._K, [self._to_K(c) for c in coefs])
|
||||
|
||||
def to_M(self, a):
|
||||
if self._to_A is not None:
|
||||
a = self._to_A(a)
|
||||
return matrix( self._K, [self._coords_over_center(a*v)
|
||||
for v in self._basis_over_center]).transpose()
|
||||
|
||||
def from_M(self, mat):
|
||||
if self._eq is None:
|
||||
self._eq = matrix([sum([list((self._coords_over_center(a*b)))
|
||||
for b in self._basis_over_center],[])
|
||||
for a in self.A.basis()])
|
||||
target = vector(mat.transpose().list())
|
||||
sol = self._eq.solve_left(target)
|
||||
res = sum([self._from_K(s)*a for s, a in zip(sol, self.A.basis())])
|
||||
if self._from_A is not None:
|
||||
res = self._from_A(res)
|
||||
return res
|
||||
|
||||
|
||||
def full_split(A):
|
||||
r"""
|
||||
A is any associative algebra over a finite field.
|
||||
Returns the wedderburn_malcev complement S of A, and then a list of
|
||||
splittings of all the simple factors of S.
|
||||
"""
|
||||
S = wedderburn_malcev_complement(A)
|
||||
ss_split = wedderburn_splitting(S[0])
|
||||
splits = [SplitAlgebra(ss[0], ss[1], ss[2]) for ss in ss_split]
|
||||
return S, splits
|
||||
|
||||
|
||||
def random_central_simple_algebra(k, n):
|
||||
Mat = MatrixSpace(k, n)
|
||||
while True:
|
||||
Isom = matrix(k, n**2, n**2, [k.random_element() for _ in range(n**4)])
|
||||
if Isom.is_unit():
|
||||
break
|
||||
basis = [Mat(c) for c in Isom.columns()]
|
||||
table = [matrix([Isom.solve_right(vector(bi * bj)) for bi in basis]) for bj in basis]
|
||||
category = Algebras(k).FiniteDimensional().WithBasis().Semisimple()
|
||||
A = FiniteDimensionalAlgebra(k, table, category=category)
|
||||
return A, Isom
|
|
@ -47,7 +47,7 @@ from sage.modules.free_module_element import vector
|
|||
from sage.categories.all import Algebras
|
||||
from sage.algebras.all import FiniteDimensionalAlgebra
|
||||
from vector_bundle import VectorBundle
|
||||
from vector_bundle import function_field_utility
|
||||
from vector_bundle import function_field_utility, algebras
|
||||
|
||||
class HomBundle(VectorBundle):
|
||||
r"""
|
||||
|
@ -426,3 +426,48 @@ class EndBundle(HomBundle):
|
|||
to_a = lambda mat : algebra(self.coordinates_in_h0(mat))
|
||||
from_a = lambda a : self.h0_from_vector(a.vector())
|
||||
return algebra, to_a, from_a
|
||||
|
||||
@cached_method
|
||||
def _global_algebra_split(self):
|
||||
r"""
|
||||
Return a splitting of ``self.global_algebra()``
|
||||
|
||||
OUTPUT:
|
||||
|
||||
- ``factors`` -- List of the matrix algebras that are simple
|
||||
factors of the semi-simple quotient of self
|
||||
inverse. Come with maps to and from ``self.global_algebra()``
|
||||
|
||||
EXAMPLES::
|
||||
|
||||
sage: from vector_bundle import VectorBundle
|
||||
sage: from sage.matrix.matrix_space import MatrixSpace
|
||||
sage: F.<x> = FunctionField(GF(7))
|
||||
sage: L1 = VectorBundle(F, 3 * x.zeros()[0].divisor())
|
||||
sage: L2 = VectorBundle(F, -2 * x.poles()[0].divisor())\
|
||||
....: .direct_sum_repeat(2)
|
||||
sage: V = L1.direct_sum(L2)
|
||||
sage: T = matrix(
|
||||
....: F, 3, 3,
|
||||
....: [2*x, 4, x+1, 4*x, 4*x+6, 5*x+6, 5*x+2, 5*x+3, 2*x+6])
|
||||
sage: V = V.apply_isomorphism(T)
|
||||
sage: End = V.end()
|
||||
sage: _, _, from_A = End.global_algebra()
|
||||
sage: S, factors = End._global_algebra_split()
|
||||
sage: len(factors)
|
||||
2
|
||||
sage: deg_1_index = [f.M.nrows() for f in factors].index(1)
|
||||
sage: deg_2_index = 1 - deg_1_index
|
||||
sage: f = factors[deg_1_index]
|
||||
sage: T^-1 * from_A(S[2](f.from_M(identity_matrix(GF(7),1)))) * T
|
||||
[1 0 0]
|
||||
[0 0 0]
|
||||
[0 0 0]
|
||||
sage: f = factors[deg_2_index]
|
||||
sage: T^-1 * from_A(S[2](f.from_M(identity_matrix(GF(7),2)))) * T
|
||||
[0 0 0]
|
||||
[0 1 0]
|
||||
[0 0 1]
|
||||
"""
|
||||
A, _, _ = self.global_algebra()
|
||||
return algebras.full_split(A)
|
||||
|
|
Loading…
Reference in New Issue