X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Feja%2Feuclidean_jordan_algebra.py;h=9414d2cf27fd2728ef205650b128ab37d1b83949;hb=38ca36bf7be2feb446d99c7cb85ae77e1fb5013c;hp=640048543cff7eb2e2a737f8fa49dfdb56b5991e;hpb=d6e8006679a55e9d2f0da3e8400963dfdcfb3283;p=sage.d.git diff --git a/mjo/eja/euclidean_jordan_algebra.py b/mjo/eja/euclidean_jordan_algebra.py index 6400485..9414d2c 100644 --- a/mjo/eja/euclidean_jordan_algebra.py +++ b/mjo/eja/euclidean_jordan_algebra.py @@ -5,12 +5,264 @@ are used in optimization, and have some additional nice methods beyond what can be supported in a general Jordan Algebra. """ -from sage.categories.magmatic_algebras import MagmaticAlgebras +from sage.categories.finite_dimensional_algebras_with_basis import FiniteDimensionalAlgebrasWithBasis from sage.structure.element import is_Matrix from sage.structure.category_object import normalize_names from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement +from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_morphism import FiniteDimensionalAlgebraMorphism + + +class FiniteDimensionalEuclideanJordanAlgebraMorphism(FiniteDimensionalAlgebraMorphism): + """ + A linear map between two finite-dimensional EJAs. + + This is a very thin wrapper around FiniteDimensionalAlgebraMorphism + that does only a few things: + + 1. Avoids the ``unitary`` and ``check`` arguments to the constructor + that will always be ``False``. This is necessary because these + are homomorphisms with respect to ADDITION, but the SageMath + machinery wants to check that they're homomorphisms with respect + to (Jordan) MULTIPLICATION. That obviously doesn't work. + + 2. Inputs and outputs the underlying matrix with respect to COLUMN + vectors, unlike the parent class. + + 3. Allows us to add, subtract, negate, multiply (compose), and + invert morphisms in the obvious way. + + If this seems a bit heavyweight, it is. I would have been happy to + use a the ring morphism that underlies the finite-dimensional + algebra morphism, but they don't seem to be callable on elements of + our EJA, and you can't add/multiply/etc. them. + """ + + def _add_(self, other): + """ + Add two EJA morphisms in the obvious way. + + EXAMPLES:: + + sage: J = RealSymmetricEJA(3) + sage: x = J.zero() + sage: y = J.one() + sage: x.operator() + y.operator() + Morphism from Euclidean Jordan algebra of degree 6 over Rational + Field to Euclidean Jordan algebra of degree 6 over Rational Field + given by matrix + [1 0 0 0 0 0] + [0 1 0 0 0 0] + [0 0 1 0 0 0] + [0 0 0 1 0 0] + [0 0 0 0 1 0] + [0 0 0 0 0 1] + + TESTS:: + + sage: set_random_seed() + sage: J = random_eja() + sage: x = J.random_element() + sage: y = J.random_element() + sage: (x.operator() + y.operator()) in J.Hom(J) + True + + """ + P = self.parent() + if not other in P: + raise ValueError("summands must live in the same space") + + return FiniteDimensionalEuclideanJordanAlgebraMorphism( + P, + self.matrix() + other.matrix() ) + + + def __init__(self, parent, f): + FiniteDimensionalAlgebraMorphism.__init__(self, + parent, + f.transpose(), + unitary=False, + check=False) + + + def _invert_(self): + """ + EXAMPLES:: + + sage: J = RealSymmetricEJA(2) + sage: x = J.linear_combination(zip(range(len(J.gens())), J.gens())) + sage: x.is_invertible() + True + sage: ~x.operator() + Morphism from Euclidean Jordan algebra of degree 3 over Rational + Field to Euclidean Jordan algebra of degree 3 over Rational Field + given by matrix + [-3/2 2 -1/2] + [ 1 0 0] + [-1/2 0 1/2] + sage: x.operator_matrix().inverse() + [-3/2 2 -1/2] + [ 1 0 0] + [-1/2 0 1/2] + + TESTS:: + + sage: set_random_seed() + sage: J = random_eja() + sage: x = J.random_element() + sage: not x.is_invertible() or ( + ....: (~x.operator()).matrix() == x.operator_matrix().inverse() ) + True + + """ + A = self.matrix() + if not A.is_invertible(): + raise ValueError("morphism is not invertible") + + P = self.parent() + return FiniteDimensionalEuclideanJordanAlgebraMorphism(self.parent(), + A.inverse()) + + def _lmul_(self, other): + """ + Compose two EJA morphisms using multiplicative notation. + + EXAMPLES:: + + sage: J = RealSymmetricEJA(3) + sage: x = J.zero() + sage: y = J.one() + sage: x.operator() * y.operator() + Morphism from Euclidean Jordan algebra of degree 6 over Rational + Field to Euclidean Jordan algebra of degree 6 over Rational Field + given by matrix + [0 0 0 0 0 0] + [0 0 0 0 0 0] + [0 0 0 0 0 0] + [0 0 0 0 0 0] + [0 0 0 0 0 0] + [0 0 0 0 0 0] + + TESTS:: + + sage: set_random_seed() + sage: J = random_eja() + sage: x = J.random_element() + sage: y = J.random_element() + sage: (x.operator() * y.operator()) in J.Hom(J) + True + + """ + if not other.codomain() is self.domain(): + raise ValueError("(co)domains must agree for composition") + + return FiniteDimensionalEuclideanJordanAlgebraMorphism( + self.parent(), + self.matrix()*other.matrix() ) + + + def _neg_(self): + """ + Negate this morphism. + + EXAMPLES:: + + sage: J = RealSymmetricEJA(2) + sage: x = J.one() + sage: -x.operator() + Morphism from Euclidean Jordan algebra of degree 3 over Rational + Field to Euclidean Jordan algebra of degree 3 over Rational Field + given by matrix + [-1 0 0] + [ 0 -1 0] + [ 0 0 -1] + + TESTS:: + + sage: set_random_seed() + sage: J = random_eja() + sage: x = J.random_element() + sage: -x.operator() in J.Hom(J) + True + + """ + return FiniteDimensionalEuclideanJordanAlgebraMorphism( + self.parent(), + -self.matrix() ) + + + def _repr_(self): + """ + We override only the representation that is shown to the user, + because we want the matrix to be with respect to COLUMN vectors. + + TESTS: + + Ensure that we see the transpose of the underlying matrix object: + + sage: J = RealSymmetricEJA(3) + sage: x = J.linear_combination(zip(range(len(J.gens())), J.gens())) + sage: L = x.operator() + sage: L + Morphism from Euclidean Jordan algebra of degree 6 over Rational + Field to Euclidean Jordan algebra of degree 6 over Rational Field + given by matrix + [ 0 1 2 0 0 0] + [1/2 3/2 2 1/2 1 0] + [ 1 2 5/2 0 1/2 1] + [ 0 1 0 3 4 0] + [ 0 1 1/2 2 4 2] + [ 0 0 2 0 4 5] + sage: L._matrix + [ 0 1/2 1 0 0 0] + [ 1 3/2 2 1 1 0] + [ 2 2 5/2 0 1/2 2] + [ 0 1/2 0 3 2 0] + [ 0 1 1/2 4 4 4] + [ 0 0 1 0 2 5] + + """ + return "Morphism from {} to {} given by matrix\n{}".format( + self.domain(), self.codomain(), self.matrix()) + + + def __sub__(self, other): + """ + Subtract one morphism from another using addition and negation. + + EXAMPLES:: + + sage: J = RealSymmetricEJA(2) + sage: L1 = J.one().operator() + sage: L1 - L1 + Morphism from Euclidean Jordan algebra of degree 3 over Rational + Field to Euclidean Jordan algebra of degree 3 over Rational + Field given by matrix + [0 0 0] + [0 0 0] + [0 0 0] + + TESTS:: + + sage: set_random_seed() + sage: J = random_eja() + sage: x = J.random_element() + sage: y = J.random_element() + sage: x.operator() - y.operator() in J.Hom(J) + True + + """ + return self + (-other) + + + def matrix(self): + """ + Return the matrix of this morphism with respect to a left-action + on column vectors. + """ + return FiniteDimensionalAlgebraMorphism.matrix(self).transpose() + class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): @staticmethod @@ -30,7 +282,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): raise ValueError("input is not a multiplication table") mult_table = tuple(mult_table) - cat = MagmaticAlgebras(field).FiniteDimensional().WithBasis() + cat = FiniteDimensionalAlgebrasWithBasis(field) cat.or_subcategory(category) if assume_associative: cat = cat.Associative() @@ -1003,14 +1255,37 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): return W.linear_combination(zip(self.vector(), B)) + def operator(self): + """ + Return the left-multiplication-by-this-element + operator on the ambient algebra. + + TESTS:: + + sage: set_random_seed() + sage: J = random_eja() + sage: x = J.random_element() + sage: y = J.random_element() + sage: x.operator()(y) == x*y + True + sage: y.operator()(x) == x*y + True + + """ + P = self.parent() + return FiniteDimensionalEuclideanJordanAlgebraMorphism( + Hom(P,P), + self.operator_matrix() ) + + + def operator_matrix(self): """ Return the matrix that represents left- (or right-) multiplication by this element in the parent algebra. - We have to override this because the superclass method - returns a matrix that acts on row vectors (that is, on - the right). + We implement this ourselves to work around the fact that + our parent class represents everything with row vectors. EXAMPLES: @@ -1093,7 +1368,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): sage: D = (x0^2 - x_bar.inner_product(x_bar))*D sage: D = D + 2*x_bar.tensor_product(x_bar) sage: Q = block_matrix(2,2,[A,B,C,D]) - sage: Q == x.quadratic_representation() + sage: Q == x.quadratic_representation().operator_matrix() True Test all of the properties from Theorem 11.2 in Alizadeh:: @@ -1102,8 +1377,8 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): sage: J = random_eja() sage: x = J.random_element() sage: y = J.random_element() - sage: Lx = x.operator_matrix() - sage: Lxx = (x*x).operator_matrix() + sage: Lx = x.operator() + sage: Lxx = (x*x).operator() sage: Qx = x.quadratic_representation() sage: Qy = y.quadratic_representation() sage: Qxy = x.quadratic_representation(y) @@ -1124,17 +1399,16 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): Property 3: - sage: not x.is_invertible() or ( - ....: Qx*x.inverse().vector() == x.vector() ) + sage: not x.is_invertible() or ( Qx(x.inverse()) == x ) True sage: not x.is_invertible() or ( - ....: Qx.inverse() + ....: ~Qx ....: == ....: x.inverse().quadratic_representation() ) True - sage: Qxy*(J.one().vector()) == (x*y).vector() + sage: Qxy(J.one()) == x*y True Property 4: @@ -1147,15 +1421,15 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): sage: not x.is_invertible() or ( ....: x.quadratic_representation(x.inverse())*Qx ....: == - ....: 2*x.operator_matrix()*Qex - Qx ) + ....: 2*x.operator()*Qex - Qx ) True - sage: 2*x.operator_matrix()*Qex - Qx == Lxx + sage: 2*x.operator()*Qex - Qx == Lxx True Property 5: - sage: J(Qy*x.vector()).quadratic_representation() == Qy*Qx*Qy + sage: Qy(x).quadratic_representation() == Qy*Qx*Qy True Property 6: @@ -1166,13 +1440,13 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): Property 7: sage: not x.is_invertible() or ( - ....: Qx*x.inverse().operator_matrix() == Lx ) + ....: Qx*x.inverse().operator() == Lx ) True Property 8: sage: not x.operator_commutes_with(y) or ( - ....: J(Qx*y.vector())^n == J(Qxn*(y^n).vector()) ) + ....: Qx(y)^n == Qxn(y^n) ) True """ @@ -1181,9 +1455,9 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra): elif not other in self.parent(): raise TypeError("'other' must live in the same algebra") - L = self.operator_matrix() - M = other.operator_matrix() - return ( L*M + M*L - (self*other).operator_matrix() ) + L = self.operator() + M = other.operator() + return ( L*M + M*L - (self*other).operator() ) def span_of_powers(self):