Source code for galgebra.lt

"""
Multivector Linear Transformation
"""

import sys
import inspect
import types
import itertools
from sympy import collect, expand, symbols, Matrix, Transpose, zeros, Symbol, Function, S, Add
from copy import copy
from . import printer
from . import metric
from . import mv
from . import utils
from functools import reduce

def aprint(a):
    out = ''
    for ai in a:
        out += str(ai)+','
    print('['+out[:-1]+']')
    return

def Symbolic_Matrix(root,coords=None,mode='g',f=False,sub=True):
    if sub:
        pos = '_'
    else:
        pos = '__'
    if isinstance(coords,(list,tuple)):
        n = len(coords)
        n_range = range(n)
        mat = zeros(n)
        if mode == 'g':  # General symbolic matrix
            for row in n_range:
                row_index = str(coords[row])
                for col in n_range:
                    col_index = str(coords[col])
                    element = root + pos + row_index + col_index
                    if not f:
                        mat[row,col] = Symbol(element,real=True)
                    else:
                       mat[row,col] = Function(element)(*coords)

        elif mode == 's':  # Symmetric symbolic matrix
            for row in n_range:
                row_index = str(coords[row])
                for col in n_range:
                    col_index = str(coords[col])
                    if row <= col:
                        element = root + pos + row_index + col_index
                    else:
                        element = root + pos + col_index + row_index
                    if not f:
                        mat[row,col] = Symbol(element,real=True)
                    else:
                       mat[row,col] = Function(element)(*coords)

        elif mode == 'a':  # Asymmetric symbolic matrix
            for row in n_range:
                row_index = str(coords[row])
                for col in n_range:
                    col_index = str(coords[col])
                    if row <= col:
                        sign = S(1)
                        element = root + pos + row_index + col_index
                    else:
                        sign = -S(1)
                        element = root + pos + col_index + row_index
                    if row == col:
                        sign = S(0)
                    if not f:
                        mat[row,col] = sign * Symbol(element,real=True)
                    else:
                       mat[row,col] = sign * Function(element)(*coords)
        else:
            raise ValueError('In Symbolic_Matrix mode = ' + str(mode))
    else:
        raise ValueError('In Symbolic_Matrix coords = ' + str(coords))
    return mat


def Matrix_to_dictionary(mat_rep,basis):
    # Convert matrix representation of linear transformation to dictionary
    dict_rep = {}
    n = len(basis)
    if mat_rep.rows != n or mat_rep.cols != n:
        raise ValueError('Matrix and Basis dimensions not equal for Matrix = ' + str(mat_rep))
    n_range = list(range(n))
    for row in n_range:
        dict_rep[basis[row]] = S(0)
        for col in n_range:
            dict_rep[basis[row]] += mat_rep[col,row]*basis[col]
    return dict_rep

def Dictionary_to_Matrix(dict_rep, ga):
    # Convert dictionary representation of linear transformation to matrix
    basis = list(dict_rep.keys())
    n = len(basis)
    n_range = list(range(n))
    lst_mat = []  # list representation of sympy matrix
    for row in n_range:
        e_row = ga.basis[row]
        lst_mat_row = n * [S(0)]

        if e_row in basis:  # If not in basis row all zeros
            (coefs,bases) = metric.linear_expand(dict_rep[e_row])
            for (coef,base) in zip(coefs,bases):
                index = ga.basis.index(base)
                lst_mat_row[index] = coef

        lst_mat.append(lst_mat_row)
    return Transpose(Matrix(lst_mat))

class Lt(object):

    mat_fmt = False
    init_slots = {'ga': (None, 'Name of metric (geometric algebra)'),
                  'f': (False, 'True if Lt if function of coordinates.'),
                  'mode': ('g', 'g:general, s:symmetric, a:antisymmetric transformation.')}

    @staticmethod
    def setup(ga):
        #coords = [Symbol('mu_' + str(x)) for x in ga.coords]
        coords = ga.coords
        x = sum([coords[i] * ga.basis[i] for i in ga.n_range])
        return coords, x

    @staticmethod
    def format(mat_fmt=False):
        Lt.mat_fmt = mat_fmt
        return

    def __init__(self, *kargs, **kwargs):
        """
        Except for the spinor representation the linear transformation
        is stored as a dictionary with basis vector keys and vector
        values self.lt_dict so that a is a vector a = a^{i}e_{i} then

            self(a) = a^{i} * self.lt_dict[e_{i}].

        For the spinor representation the linear transformation is
        stored as the even multivector self.R so that if a is a
        vector

            self(a) = self.R * a * self.R.rev().

        For the general representation of a linear transformation the
        linear transformation is represented as a dictionary self.lt_dict
        where the keys are the basis symbols, {e_i}, and the dictionary
        entries are the object vector images (linear combination of sympy
        non-commutative basis symbols) of the keys so that if L is the
        linear transformation then

            L(e_i) = self.Ga.mv(L.lt_dict[e_i])

        """

        kwargs = metric.test_init_slots(Lt.init_slots, **kwargs)

        mat_rep = kargs[0]
        ga = kwargs['ga']
        self.fct_flg = kwargs['f']
        self.mode = kwargs['mode']  # General g, s, or a transformation
        self.Ga = ga
        self.coords = ga.lt_coords
        self.X = ga.lt_x
        self.spinor = False
        self.rho_sq = None

        self.lt_dict = {}
        self.mv_dict = None
        self.mat = None

        self.Ga.inverse_metric()  # g^{-1} needed for standard matrix representation

        if isinstance(mat_rep, tuple):  # tuple input
            for key in mat_rep:
                self.lt_dict[key] = mat_rep[key]

        elif isinstance(mat_rep, dict):  # Dictionary input
            for key in mat_rep:
                self.lt_dict[key] = mat_rep[key]

        elif isinstance(mat_rep, list):  # List of lists input
            if not isinstance(mat_rep[0], list):
                for (lt_i, base) in zip(mat_rep, self.Ga.basis):
                    self.lt_dict[base] = lt_i
            else:
                #mat_rep = map(list, zip(*mat_rep))  # Transpose list of lists
                for (row, base1) in zip(mat_rep, self.Ga.basis):
                    tmp = 0
                    for (col, base2) in zip(row, self.Ga.basis):
                        tmp += col * base2
                    self.lt_dict[base1] = tmp

        elif isinstance(mat_rep, Matrix):  # Matrix input
            self.mat = mat_rep
            mat_rep = self.mat * self.Ga.g_inv
            self.lt_dict = Matrix_to_dictionary(mat_rep, self.Ga.basis)

        elif isinstance(mat_rep, mv.Mv):  # Spinor input
            self.spinor = True
            self.R = mat_rep
            self.Rrev = mat_rep.rev()
            self.rho_sq = self.R * self.Rrev
            if self.rho_sq.is_scalar():
                self.rho_sq = self.rho_sq.scalar()
                if self.rho_sq == S(1):
                    self.rho_sq = None
            else:
                raise ValueError('In Spinor input for Lt, S*S.rev() not a scalar!\n')

        elif utils.isstr(mat_rep):  # String input
             Amat = Symbolic_Matrix(mat_rep, coords=self.Ga.coords,mode=self.mode,f=self.fct_flg)
             self.__init__(Amat, ga=self.Ga)

        else:  # Linear multivector function input
            # F is a multivector function to be tested for linearity
            F = mat_rep
            a = mv.Mv('a', 'vector', ga=self.Ga)
            b = mv.Mv('b', 'vector', ga=self.Ga)
            if F(a + b) == F(a) + F(b):
                self.lt_dict = {}
                for base in self.Ga.basis:
                    self.lt_dict[base] = (F(mv.Mv(base, ga=self.Ga))).obj
                    if not self.lt_dict[base].is_vector():
                        raise ValueError(str(mat_rep) + ' is not supported for Lt definition\n')
            else:
                raise ValueError(str(mat_rep) + ' is not supported for Lt definition\n')

    def __call__(self, v, obj=False):

        if isinstance(v, mv.Mv) and self.Ga.name != v.Ga.name:
                raise ValueError('In Lt call Lt and argument refer to different vector spaces')

        if self.spinor:
            if not isinstance(v, mv.Mv):
                v = mv.Mv(v, ga=self.Ga)
            if self.rho_sq == None:
                R_v_Rrev = self.R * v * self.Rrev
            else:
                R_v_Rrev = self.rho_sq * self.R * v * self.Rrev
            if obj:
                return R_v_Rrev.obj
            else:
                return R_v_Rrev

        if isinstance(v, mv.Mv):
            if v.is_vector():
                lt_v = v.obj.xreplace(self.lt_dict)
                if obj:
                    return lt_v
                else:
                    return mv.Mv(lt_v, ga=self.Ga)
            else:
                mv_obj = v.obj
        else:
            mv_obj = mv.Mv(v, ga=self.Ga).obj

        if self.mv_dict is None:  # Build dict for linear transformation of multivector
            self.mv_dict = copy(self.lt_dict)
            for key in self.Ga.blades[2:]:
                for blade in key:
                    index = self.Ga.blades_to_indexes_dict[blade]
                    lt_blade = self(self.Ga.basis[index[0]], obj=True)
                    for i in index[1:]:
                        lt_blade = self.Ga.wedge(lt_blade, self(self.Ga.basis[i], obj=True))
                    self.mv_dict[blade] = lt_blade

        lt_v = mv_obj.xreplace(self.mv_dict)
        if obj:
            return lt_v
        else:
            return mv.Mv(lt_v, ga=self.Ga)

    def __add__(self, LT):

        if self.Ga.name != LT.Ga.name:
            raise ValueError("Attempting addition of Lt's from different geometric algebras")

        self_add_LT = copy(self.lt_dict)
        for key in list(LT.lt_dict.keys()):
            if key in self_add_LT:
                self_add_LT[key] = collect(self_add_LT[key] + LT.lt_dict[key], self.Ga.basis)
            else:
                self_add_LT[key] = LT.lt_dict[key]
        return(Lt(self_add_LT, ga=self.Ga))

    def __sub__(self, LT):

        if self.Ga.name != LT.Ga.name:
            raise ValueError("Attempting subtraction of Lt's from different geometric algebras")

        self_add_LT = copy(self.lt_dict)
        for key in list(LT.lt_dict.keys()):
            if key in self_add_LT:
                self_add_LT[key] = collect(self_add_LT[key] - LT.lt_dict[key], self.Ga.basis)
            else:
                self_add_LT[key] = -LT.lt_dict[key]
        return(Lt(self_add_LT, ga=self.Ga))

    def __mul__(self, LT):

        if isinstance(LT, Lt):

            if self.Ga.name != LT.Ga.name:
                raise ValueError("Attempting multiplication of Lt's from different geometric algebras")
            self_mul_LT = {}
            for base in LT.lt_dict:
                self_mul_LT[base] = self(LT(base, obj=True), obj=True)
            for key in self_mul_LT:
                self_mul_LT[key] = expand(self_mul_LT[key]).collect(self.Ga.basis)
            return(Lt(self_mul_LT, ga=self.Ga))
        else:
            self_mul_LT = {}
            for key in self.lt_dict:
                self_mul_LT[key] = LT * self.lt_dict[key]
            return(Lt(self_mul_LT, ga=self.Ga))

    def __rmul__(self, LT):

        if not isinstance(LT, Lt):
            self_mul_LT = {}
            for key in self.lt_dict:
                self_mul_LT[key] = LT * self.lt_dict[key]
            return(Lt(self_mul_LT, ga=self.Ga))
        else:
            raise TypeError('Cannot have LT as left argument in Lt __rmul__\n')

    def __repr__(self):
        return str(self)

    def _repr_latex_(self):
        latex_str = printer.GaLatexPrinter.latex(self)
        if r'\begin{align*}' not in latex_str:
            latex_str = r'\begin{equation*} ' + latex_str + r' \end{equation*}'
        return latex_str

    def det(self):  # det(L) defined by L(I) = det(L)I

        lt_I = self(self.Ga.i, obj=True)
        det_lt_I = lt_I.subs(self.Ga.i.obj, S(1))
        return(det_lt_I)

    def tr(self):  # tr(L) defined by tr(L) = grad|L(x)

        connect_flg = self.Ga.connect_flg
        self.Ga.connect_flg = False

        F_x = mv.Mv(self(self.Ga.lt_x, obj=True), ga=self.Ga)
        tr_F = (self.Ga.grad | F_x).scalar()
        self.Ga.connect_flg = connect_flg
        return(tr_F)

    def adj(self):

        self_adj = []
        self.Ga.dot_mode = '|'
        for e_j in self.Ga.basis:
            s = S(0)
            for (e_i, er_i) in zip(self.Ga.basis, self.Ga.r_basis):
                s += er_i * self.Ga.dot(e_j, self(e_i, obj=True))
            if self.Ga.is_ortho:
                self_adj.append(expand(s))
            else:
                self_adj.append(expand(s) / self.Ga.e_sq)
        return Lt(self_adj, ga=self.Ga)

    def inv(self):
        if self.spinor:
            Lt_inv = Lt(self.Rrev,ga=self.Ga)
            Lt_inv.rho_sq = S(1)/(self.rho_sq**2)
        else:
            raise ValueError('Lt inverse currently implemented only for spinor!\n')
        return Lt_inv

    def Lt_str(self):

        if self.spinor:
            return 'R = ' + str(self.R)
        else:
            pre = 'Lt('
            s = ''
            for base in self.Ga.basis:
                if base in self.lt_dict:
                    s += pre + str(base) + ') = ' + str(mv.Mv(self.lt_dict[base], ga=self.Ga)) + '\n'
                else:
                    s += pre + str(base) + ') = 0\n'
            return s[:-1]

    def Lt_latex_str(self):

        if self.spinor:
            s = '\\left \\{ \\begin{array}{ll} '
            for base in self.Ga.basis:
                str_base = printer.latex(base)
                s += 'L \\left ( ' + str_base + '\\right ) =& ' + printer.latex(self.R * mv.Mv(base, ga=self.Ga) * self.Rrev) + ' \\\\ '
            s = s[:-3] + ' \\end{array} \\right \\} \n'
            return s
        else:
            s = '\\left \\{ \\begin{array}{ll} '
            for base in self.Ga.basis:
                str_base = printer.latex(base)
                if base in self.lt_dict:
                    s += 'L \\left ( ' + str_base + '\\right ) =& ' + printer.latex(mv.Mv(self.lt_dict[base], ga=self.Ga)) + ' \\\\ '
                else:
                    s += 'L \\left ( ' + str_base + '\\right ) =& 0 \\\\ '
            s = s[:-3] + ' \\end{array} \\right \\} \n'
            return s

    def Fmt(self, fmt=1, title=None):

        if printer.isinteractive():
            return self

        latex_str = printer.GaLatexPrinter.latex(self)

        r"""
        if printer.GaLatexPrinter.ipy:
            if title is None:
                if r'\begin{align*}' not in latex_str:
                    latex_str = r'\begin{equation*} ' + latex_str + r' \end{equation*}'
            else:
                if r'\begin{align*}' not in latex_str:
                    latex_str = r'\begin{equation*} ' + title + ' = ' + latex_str + r' \end{equation*}'
                else:
                    latex_str = latex_str.replace(r'\begin{align*}', r'\begin{align*} ' + title)
                    latex_str = latex_str.replace('&', '=&', 1)

            from IPython.core.display import display, Math
            display(Math(latex_str))
        else:
            if title is not None:
                return title + ' = ' + latex_str
            else:
                    return latex_str
        """
        if title is not None:
            return title + ' = ' + latex_str
        else:
            return latex_str

    def __str__(self):
        if printer.GaLatexPrinter.latex_flg:
            Printer = printer.GaLatexPrinter
        else:
            Printer = printer.GaPrinter
        return Printer().doprint(self)

    def matrix(self):

        if self.mat is not None:
            return self.mat
        else:
            if self.spinor:
                self.lt_dict = {}
                for base in self.Ga.basis:
                    self.lt_dict[base] = self(base).simplify()
                self.spinor = False
                mat = self.matrix()
                self.spinor = True
                return(mat)
            else:
                """
                mat_rep = []
                for base in self.Ga.basis:
                    if base in self.lt_dict:
                        row = []
                        image = (self.lt_dict[base])
                        if isinstance(image, mv.Mv):
                            image = image.obj
                        (coefs, bases) = metric.linear_expand(image)
                        for base in self.Ga.basis:
                            try:
                                i = bases.index(base)
                                row.append(coefs[i])
                            except:
                                row.append(0)
                        mat_rep.append(row)
                    else:
                        mat_rep.append(self.Ga.n * [0])
                return(Matrix(mat_rep).transpose())
                """
                self.mat = Dictionary_to_Matrix(self.lt_dict, self.Ga) * self.Ga.g
                return self.mat


[docs]class Mlt(object): """ A multilinear transformation (mlt) is a multilinear multivector function of a list of vectors (*kargs) F(v_1,...,v_r) where for any argument slot j we have (a is a scalar and u_j a vector) F(v_1,...,a*v_j,...,v_r) = a*F(v_1,...,v_j,...,v_r) F(v_1,...,v_j+u_j,...,v_r) = F(v_1,...,v_j,...,v_r) + F(v_1,...,u_j,...,v_r). If F and G are two mlt's with the same number of argument slots then the sum is (F+G)F(v_1,...,v_r) = F(v_1,...,v_r) + G(v_1,...,v_r). If F and G two mlt's with r and s argument slots then their product is (F*G)(v_1,...,v_r,...,v_(r+s)) = F(v_1,...,v_r)*G(v_(r+1),...,v_(r+s)), where * is any of the multivector multiplicative operations. The derivative of a mlt with is defined as the directional derivative with respect with respect to the coordinate vector (we assume F is implicitely a function of the coordinates) F(v_1,...,v_r;v_(r+1)) = (v_(r+1)|grad)F(v_1,...,v_j,...,v_r). The contraction of a mlt between slots j and k is defined as the geometric derivative of F with respect to slot k and the inner geometric derivative with respect to slot j (this gives the standard tensor definition of contraction for the case that F is a scalar function) Contract(i,j,F) = grad_i|(grad_j*F(v_1,...,v_i,...,v_j,...,v_r)) = grad_j|(grad_i*F(v_1,...,v_i,...,v_j,...,v_r)). This returns a mlt with slot i and j removed. """ @staticmethod def subs(Ga, anew): # Generate coefficient substitution list for new Mlt slot # vectors (arguments) where anew is a list of slot vectors # to be substituted for the old slot vectors. # This is used when one wishes to substitute specific vector # values into the Mlt such as the basis/reciprocal basis vectors. sub_lst = [] i = 0 for a in anew: acoefs = a.get_coefs(1) sub_lst += list(zip(Ga.pdiffs[i], acoefs)) i += 1 return sub_lst @staticmethod def increment_slots(nargs, Ga): # Increment cache of available slots (vector variables) if needed for Mlt class n_a = len(Ga.a) if n_a < nargs: for i in range(n_a, nargs): # New slot variable with coefficients a_{n_a}__k a = Ga.mv('a_' + str(i + 1), 'vector') # Append new slot variable a_j Ga.a.append(a) # Append slot variable coefficients a_j__k for purpose # of differentiation coefs = a.get_coefs(1) Ga.pdiffs.append(coefs) Ga.acoefs += coefs return @staticmethod def extact_basis_indexes(Ga): base_indexes = [] for base in Ga.basis: base_str = str(base) base_str = base_str.replace(r'\boldsymbol','') base_str = base_str.replace('{','') base_str = base_str.replace('}','') i = base_str.find('_') + 1 if i == 0: base_indexes.append(base_str) else: if base_str[i] == '_': i += 1 base_indexes.append(base_str[i:]) return base_indexes def Mlt_str(self): return str(self.fvalue) def Mlt_latex_str(self): if self.nargs <= 1: return printer.latex(self.fvalue) expr_lst = Mlt.expand_expr(self.fvalue,self.Ga) latex_str = '\\begin{align*} ' first = True cnt = 1 # Component count on line for term in expr_lst: coef_str = str(term[0]) coef_latex = printer.latex(term[0]) term_add_flg = isinstance(term[0],Add) if term_add_flg: coef_latex = r'\left ( ' + coef_latex + r'\right ) ' if first: first = False else: if coef_str[0].strip() is not '-' or term_add_flg: coef_latex = ' + ' + coef_latex for aij in term[1]: coef_latex += printer.latex(aij) + ' ' if cnt == 1: latex_str += ' & ' + coef_latex else: latex_str += coef_latex if cnt%self.lcnt == 0: latex_str += '\\\\ ' cnt = 1 else: cnt += 1 if self.lcnt == len(expr_lst) or self.lcnt == 1: latex_str = latex_str[:-3] latex_str = latex_str + ' \\end{align*} \n' return latex_str
[docs] def Fmt(self, lcnt=1, title=None): """ Set format for printing of Tensors - lcnt = Number of components per line Usage for tensor T example is - T.fmt('2','T') output is print 'T = '+str(A) with two components per line. Works for both standard printing and for latex. """ self.lcnt = lcnt latex_str = printer.GaLatexPrinter.latex(self) self.lcnt = 1 if printer.GaLatexPrinter.ipy: if title is None: if r'\begin{align*}' not in latex_str: latex_str = r'\begin{equation*} ' + latex_str + r' \end{equation*}' else: if r'\begin{align*}' not in latex_str: latex_str = r'\begin{equation*} ' + title + ' = ' + latex_str + r' \end{equation*}' else: latex_str = latex_str.replace(r'\begin{align*}', r'\begin{align*} ' + title) latex_str = latex_str.replace('&', '=&', 1) from IPython.core.display import display, Math display(Math(latex_str)) else: if title is not None: print(title + ' = ' + latex_str) else: print(latex_str) return
@staticmethod def expand_expr(expr,ga): lst_expr = [] expr = expand(expr) for term in expr.args: coef = S(1) a_lst = [] for factor in term.args: if factor in ga.acoefs: a_lst.append(factor) else: coef *= factor a_lst = tuple([x for x in a_lst if x in ga.acoefs]) b_lst = tuple([ga.acoefs.index(x) for x in a_lst]) lst_expr.append((coef,a_lst,b_lst)) lst_expr = sorted(lst_expr, key=lambda x: x[2]) new_lst_expr = [] previous = (-1,) first = True a = None for term in lst_expr: if previous == term[2]: coef += term[0] previous = term[2] else: if not first: new_lst_expr.append((coef, a)) else: first = False coef = term[0] previous = term[2] a = term[1] new_lst_expr.append((coef, a)) return new_lst_expr def __init__(self, f, Ga, args, fct=False): # f is a function, a multivector, a string, or a component expression # self.f is a function or None such as T | a_1 where T and a_1 are vectors # self.fvalue is a component expression such as # T_x*a_1__x+T_y*a_1__y+T_z*a_1__z for a rank 1 tensor in 3 space and all # symbols are sympy real scalar symbols self.Ga = Ga self.args = args self.nargs = len(args) self.lcnt = 1 if isinstance(f, mv.Mv): if f.is_vector(): # f is vector T = f | a1 if self.nargs != 1: raise ValueError('For mlt nargs != 1 for vector!\n') Ga.make_grad(self.args) self.fvalue = (f | self.args[0]).obj self.f = None else: # To be inplemented for f a general pure grade mulitvector self.nargs = len(args) self.fvalue = f self.f = None elif isinstance(f, Lt): # f is linear transformation T = a1 | f(a2) if self.nargs != 2: raise ValueError('For mlt nargs != 2 for linear transformation!\n') Ga.make_grad(self.args) self.fvalue = (self.args[0] | f(self.args[1])).obj self.f = None elif utils.isstr(f) and args is not None: self.f = None if isinstance(args,(list,tuple)): self.args = args self.nargs = len(args) else: self.args = [args] self.nargs = 1 if self.nargs > 1: # General tensor of rank > 1 t_indexes = self.nargs * [Mlt.extact_basis_indexes(self.Ga)] print(t_indexes) print(self.Ga.Pdiffs) self.fvalue = 0 for (t_index,a_prod) in zip(itertools.product(*t_indexes), itertools.product(*self.Ga.Pdiffs)): if fct: # Tensor field coef = Function(f+'_'+''.join(map(str,t_index)),real=True)(*self.Ga.coords) else: # Constant Tensor coef = symbols(f+'_'+''.join(map(str,t_index)),real=True) coef *= reduce(lambda x, y: x*y, a_prod) self.fvalue += coef else: # General tensor of rank = 1 self.fvalue = 0 for (t_index,a_prod) in zip(Mlt.extact_basis_indexes(self.Ga),self.Ga.pdiffs[0]): if fct: # Tensor field coef = Function(f+'_'+''.join(map(str,t_index)),real=True)(*self.Ga.coords) else: # Constant Tensor coef = symbols(f+'_'+''.join(map(str,t_index)),real=True) self.fvalue += coef * a_prod else: if isinstance(f, types.FunctionType): # Tensor defined by general multi-linear function args, _kargs, _kwargs, _defaults = inspect.getargspec(f) self.nargs = len(args) self.f = f Mlt.increment_slots(self.nargs, Ga) self.fvalue = f(*tuple(Ga.a[0:self.nargs])) else: # Tensor defined by component expression self.f = None self.nargs = len(args) Mlt.increment_slots(self.nargs, Ga) self.fvalue = f def __str__(self): if printer.GaLatexPrinter.latex_flg: Printer = printer.GaLatexPrinter else: Printer = printer.GaPrinter return Printer().doprint(self) def __call__(self, *kargs): if len(kargs) == 0: return self.fvalue if self.f is not None: return self.f(*kargs) else: sub_lst = [] for (x, ai) in zip(kargs, self.Ga.pdiffs): for (r_base, aij) in zip(self.Ga.r_basis_mv, ai): sub_lst.append((aij, (r_base | x).scalar())) return self.fvalue.subs(sub_lst,simultaneous=True) def __add__(self, X): if isinstance(Mlt, X): if self.nargs == X.nargs: return Mlt(self.fvalue + X.fvalue, self.Ga, self.nargs) else: raise ValueError('In Mlt add number of args not the same\n') else: raise TypeError('In Mlt add second argument not an Mkt\n') def __sub__(self, X): if isinstance(Mlt, X): if self.nargs == X.nargs: return Mlt(self.fvalue - X.fvalue, self.Ga, self.nargs) else: raise ValueError('In Mlt sub number of args not the same\n') else: raise TypeError('In Mlt sub second argument not an Mlt\n') def __mul__(self, X): if isinstance(X, Mlt): nargs = self.nargs + X.nargs Mlt.increment_slots(nargs, self.Ga) self_args = self.Ga.a[:self.nargs] X_args = X.Ga.a[self.nargs:nargs] value = expand(self(*self_args) * X(*X_args)) return Mlt(value, self.Ga, nargs) else: return Mlt(X * self.fvalue, self.Ga, self.nargs) def __xor__(self, X): if isinstance(X, Mlt): nargs = self.nargs + X.nargs Mlt.increment_slots(nargs, self.Ga) value = self(*self.Ga.a[:self.nargs]) ^ X(X.Ga.a[self.nargs:nargs]) return Mlt(value, self.Ga, nargs) else: return Mlt(X * self.fvalue, self.Ga, self.nargs) def __or__(self, X): if isinstance(X, Mlt): nargs = self.nargs + X.nargs Mlt.increment_slots(nargs, self.Ga) value = self(*self.Ga.a[:self.nargs]) | X(X.Ga.a[self.nargs:nargs]) return Mlt(value, self.Ga, nargs) else: return Mlt(X * self.fvalue, self.Ga, self.nargs) def _repr_latex_(self): latex_str = printer.GaLatexPrinter.latex(self) if r'\begin{align*}' not in latex_str: latex_str = r'\begin{equation*} ' + latex_str + r' \end{equation*}' return latex_str def dd(self): Mlt.increment_slots(self.nargs + 1, self.Ga) dd_fvalue = (self.Ga.a[self.nargs] | self.Ga.grad) * self.fvalue return Mlt(dd_fvalue, self.Ga, self.nargs + 1) def pdiff(self, slot): # Take geometric derivative of mlt with respect to slot argument self.Ga.dslot = slot - 1 return self.Ga.grad * self.Ga.mv(self.fvalue) @staticmethod def remove_slot(mv, slot, nargs, ga): if slot == nargs: return mv for islot in range(slot, nargs): mv = mv.subs(list(zip(ga.pdiffs[islot], ga.pdiffs[islot - 1]))) return mv def contract(self, slot1, slot2): min_slot = min(slot1, slot2) max_slot = max(slot1, slot2) cnargs = self.nargs - 2 self.Ga.dslot = min_slot - 1 grad_self = self.Ga.grad * self.Ga.mv(self.fvalue) grad_self = Mlt.remove_slot(grad_self.obj, min_slot, self.nargs, self.Ga) self.Ga.dslot = max_slot - 2 div_grad_self = self.Ga.grad | self.Ga.mv(grad_self) div_grad_self = Mlt.remove_slot(div_grad_self.obj, max_slot - 1, self.nargs - 1, self.Ga) return Mlt(div_grad_self, self.Ga, cnargs) def cderiv(self): Mlt.increment_slots(self.nargs + 1, self.Ga) agrad = self.Ga.a[self.nargs] | self.Ga.grad CD = Mlt((agrad * self.Ga.mv(self.fvalue)).obj, self.Ga, self.nargs + 1) if CD != 0: CD = CD.fvalue for i in range(self.nargs): args = self.Ga.a[:self.nargs] tmp = agrad * self.Ga.a[i] if tmp.obj != 0: args[i] = tmp CD = CD - self(*args) CD = Mlt(CD, self.Ga, self.nargs + 1) return CD def expand(self): self.fvalue = expand(self.fvalue) return self def comps(self): basis = self.Ga.mv() rank = self.nargs ndim = len(basis) i_indexes = rank*[list(range(ndim))] indexes = rank*[basis] i = 1 output = '' for (e,i_index) in zip(itertools.product(*indexes),itertools.product(*i_indexes)): if i_index[-1] % ndim == 0: print('') output += str(i)+':'+str(i_index)+':'+str(self(*e)) + '\n' i += 1 return output
if __name__ == "__main__": pass