Introducción a itensor
======================

Maxima implementa dos tipos diferentes de manipulación simbólica de tensores: la manipulación de componentes (paquete ctensor) y la manipulación indexada (paquete itensor).

Véase más abajo la nota sobre 'notación tensorial'.

La manipulación de componentes significa que los objetos geométricos tensoriales se representan como arreglos (arrays) o matrices. Operaciones tensoriales como la contracción o la diferenciación covariante se llevan a cabo sumando índices mudos con la sentencia do. Esto es, se realizan operaciones directamente con las componentes del tensor almacenadas en un arreglo o matriz.

La manipulación indexada de tensores se lleva a cabo mediante la representación de los tensores como funciones de sus índices covariantes, contravariantes y de derivadas. Operaciones tensoriales como la contracción o la diferenciación covariante se realizan manipulando directamente los índices, en lugar de sus componentes asociadas.

Estas dos técnicas para el tratamiento de los procesos diferenciales, algebraicos y analíticos en el contexto de la geometría riemanniana tienen varias ventajas y desventajas que surgen según la naturaleza y dificultad del problema que está abordando el usuario. Sin embargo, se deben tener presentes las siguientes características de estas dos técnicas:

La representación de los tensores y sus operaciones en términos de sus componentes facilita el uso de paquete ctensor. La especificación de la métrica y el cálculo de los tensores inducidos e invariantes es inmediato. Aunque toda la potencia de simplificación de Maxima se encuentra siempre a mano, una métrica compleja con dependencias funcionales y de coordenadas intrincada, puede conducir a expresiones de gran tamaño en las que la estructura interna quede oculta. Además, muchos cálculos requieren de expresiones intermedias que pueden provocar la detención súbita de los programas antes de que se termine el cálculo. Con la experiencia, el usuario podrá evitar muchas de estas dificultades.

Devido a la forma en que los tensores y sus operaciones se representan en términos de operaciones simbólicas con sus índices, expresiones que serían intratables en su representación por componentes pueden en ocasiones simplificarse notablemente utilizando las rutinas especiales para objetos simétricos del paquete itensor. De esta manera, la estructura de expresiones grandes puede hacerse más transparente. Por otro lado, debido a la forma especial de la representación indexada de tensores en itensor, en algunos casos el usuario encontrará dificultades con la especificación de la métrica o la definición de funciones.

Notación tensorial
------------------

Hasta ahora, el paquete itensor de Maxima utilizaba una notación que algunas veces llevaba a una ordenación incorrecta de los índices. Por ejemplo:

     (%i2) imetric(g);
     (%o2)                                done
     (%i3) ishow(g([],[j,k])*g([],[i,l])*a([i,j],[]))$
                                      i l  j k
     (%t3)                           g    g    a
                                                i j
     (%i4) ishow(contract(%))$
                                           k l
     (%t4)                                a

Este resultado no es correcto a menos que a sea un tensor simétrico. La razón por la que esto ocurre es que aunque itensor mantenga correctamente el orden dentro del conjunto de índices covariantes y contravariantes, una vez un índice sea aumentado o disminuido, su posición relativa al otro conjunto de índices se pierde.

Para evitar este problema, se ha desarrollado una notación totalmente compatible con la anterior.En esta notación, los índices contravariantes se insertan en las posiciones correctas en la lista de índices covariantes, pero precedidos del signo negativo.

En esta notación, el ejemplo anterior da el resultado correcto:

     (%i5) ishow(g([-j,-k],[])*g([-i,-l],[])*a([i,j],[]))$
                                      i l       j k
     (%t5)                           g    a    g
                                           i j
     (%i6) ishow(contract(%))$
                                           l k
     (%t6)                                a

El único código que hace uso de esta notación es la función lc2kdt.

Devido a que este código es nuevo, puede contener errores.

Manipulación indexada de tensores
---------------------------------

El paquete itensor se carga haciendo load(itensor). Para acceder a las demos se hará demo(tensor).

En el paquete itensor un tensor se representa como un objeto indexado, esto es, como una función de tres grupos de índices: los covariantes, los contravariantes y los de derivadas. Los índices covariantes se especifican mediante una lista que será el primer argumento del objeto indexado, siendo los índices contravariantes otra lista que será el segundo argumento del mismo objeto indexado. Si al objeto indexado le falta cualquiera de estos grupos de índices, entonces se le asignará al argumento correspondiente la lista vacía [].  Así, g([a,b],[c]) representa un objeto indexado llamado g, el cual tiene dos índices covariantes (a,b), un índice contravariante (c) y no tiene índices de derivadas.

Los índices de derivadas, si están presentes, se añaden como argumentos adicionales a la función simbólica que representa al tensor. Se pueden especificar explícitamente por el usuario o pueden crearse durante el proceso de diferenciación respecto de alguna coordenada. Puesto que la diferenciación ordinaria es conmutativa, los índices de derivadas se ordenan alfanuméricamente, a menos que la variable iframe_flag valga true, indicando que se está utilizando una métrica del sistema de referencia. Esta ordenación canónica hace posible que Maxima reconozca, por ejemplo, que t([a],[b],i,j) es lo mismo que t([a],[b],j,i). La diferenciación de un objeto indexado con respecto de alguna coordenada cuyo índice no aparece como argumento de dicho objeto indexado, dará como resultado cero. Esto se debe a que Maxima no sabe si el tensor representado por el objeto indexado depende implícitamente de la coordenada correspondiente. Modificando la función diff de Maxima en itensor, se da por hecho que todos los objetos indexados dependen de cualquier variable de diferenciación, a menos que se indique lo contrario. Esto hace posible que la convención sobre la sumación se extienda a los índices de derivadas. El paquete itensor trata a los índices de derivadas como covariantes.

Las siguientes funciones forman parte del paquete itensor para la manipulación indexada de vectores. En lo que respecta a las rutinas de simplificación, no se considera en general que los objetos indexados tengan propiedades simétricas. Esto puede cambiarse reasignando a la variable allsym[false] el valor true, con lo cual los objetos indexados se considerarán simétricos tanto respecto de sus índices covariantes como contravariantes.

En general, el paquete itensor trata a los tensores como objetos opacos. Las ecuaciones tensoriales se manipulan en base a reglas algebraicas, como la simetría y la contracción. Además, en el paquete itensor hay funciones para la diferenciación covariante, la curvatura y la torsión. Los cálculos se pueden realizar respecto de una métrica del sistema de referencia móvil, dependiendo de las asignaciones dadas a la variable iframe_flag.

La siguiente sesión de ejemplo demuestra cómo cargar el paquete itensor, especificar el nombre de la métrica y realizar algunos cálculos sencillos.

     (%i1) load(itensor);
     (%o1)      /share/tensor/itensor.lisp
     (%i2) imetric(g);
     (%o2)                                done
     (%i3) components(g([i,j],[]),p([i,j],[])*e([],[]))$
     (%i4) ishow(g([k,l],[]))$
     (%t4)                               e p
                                            k l
     (%i5) ishow(diff(v([i],[]),t))$
     (%t5)                                  0
     (%i6) depends(v,t);
     (%o6)                               [v(t)]
     (%i7) ishow(diff(v([i],[]),t))$
                                         d
     (%t7)                               -- (v )
                                         dt   i
     (%i8) ishow(idiff(v([i],[]),j))$
     (%t8)                                v
                                           i,j
     (%i9) ishow(extdiff(v([i],[]),j))$
     (%t9)                             v    - v
                                        j,i    i,j
                                       -----------
                                            2
     (%i10) ishow(liediff(v,w([i],[])))$
                                    %3          %3
     (%t10)                        v   w     + v   w
                                        i,%3    ,i  %3
     (%i11) ishow(covdiff(v([i],[]),j))$
                                                   %4
     (%t11)                        v    - v   ichr2
                                    i,j    %4      i j
     (%i12) ishow(ev(%,ichr2))$
                    %4 %5
     (%t12) v    - g      v   (e p       + e   p     - e p       - e    p
             i,j           %4     j %5,i    ,i  j %5      i j,%5    ,%5  i j

                                              + e p       + e   p    )/2
                                                   i %5,j    ,j  i %5
     (%i13) iframe_flag:true;
     (%o13)                               true
     (%i14) ishow(covdiff(v([i],[]),j))$
                                                  %6
     (%t14)                        v    - v   icc2
                                    i,j    %6     i j
     (%i15) ishow(ev(%,icc2))$
                                                  %6
     (%t15)                        v    - v   ifc2
                                    i,j    %6     i j
     (%i16) ishow(radcan(ev(%,ifc2,ifc1)))$
                  %6 %8                    %6 %8
     (%t16) - (ifg      v   ifb       + ifg      v   ifb       - 2 v
                         %6    j %8 i             %6    i j %8      i,j

                                                  %6 %8
                                             - ifg      v   ifb      )/2
                                                         %6    %8 i j
     (%i17) ishow(canform(s([i,j],[])-s([j,i])))$
     (%t17)                            s    - s
                                        i j    j i
     (%i18) decsym(s,2,0,[sym(all)],[]);
     (%o18)                               done
     (%i19) ishow(canform(s([i,j],[])-s([j,i])))$
     (%t19)                                 0
     (%i20) ishow(canform(a([i,j],[])+a([j,i])))$
     (%t20)                            a    + a
                                        j i    i j
     (%i21) decsym(a,2,0,[anti(all)],[]);
     (%o21)                               done
     (%i22) ishow(canform(a([i,j],[])+a([j,i])))$
     (%t22)                                 0

Funciones y variables para itensor
==================================

Trabajando con objetos indexados
--------------------------------

 -- Función: entertensor (NOMBRE)

     Permite crear un objeto indexado llamado NOMBRE, con cualquier número de índices tensoriales y de derivadas. Se admiten desde un único índice hasta una lista de índices. Véase el ejemplo en la descripción de covdiff.

 -- Función: changename (ANTERIOR, NUEVO, EXPR)

     Cambia el nombre de todos los objetos indexados llamados ANTERIOR a NEW en EXPR. El argumento ANTERIOR puede ser un símbolo o una lista de la forma [NOMBRE, M, N], en cuyo caso sólo los objetos indexados de llamados NOMBRE con M índices covariantes y N contravariantes se renombrarán como NUEVO.

 -- Función: listoftens

     Hace un listado de todos los tensores y sus índices en una expresión tensorial. Por ejemplo,

          (%i6) ishow(a([i,j],[k])*b([u],[],v)+c([x,y],[])*d([],[])*e)$
                                                   k
          (%t6)                        d e c    + a    b
                                            x y    i j  u,v
          (%i7) ishow(listoftens(%))$
                                         k
          (%t7)                        [a   , b   , c   , d]
                                         i j   u,v   x y

 -- Función: ishow (EXPR)

     Muestra EXPR con todos los objetos indexados que contiene, junto con los correspondientes índices covariantes (como subíndices) y contravariantes (como superíndices). Los índices de derivadas se muestran como subíndices, separados de los covariantes por una coma; véanse los múltiples ejemplos de este documento.

 -- Función: indices (EXPR)

     Devuelve una lista con dos elementos. El primer elemento es una lista con los índices libres, aquellos que aparecen una sola vez. El segundo elemento es una lista con los índices mudos en EXPR, aquellos que aparecen exactamente dos veces. Por ejemplo,

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) ishow(a([i,j],[k,l],m,n)*b([k,o],[j,m,p],q,r))$
                                          k l      j m p
          (%t2)                          a        b
                                          i j,m n  k o,q r
          (%i3) indices(%);
          (%o3)                 [[l, p, i, n, o, q, r], [k, j, m]]

     Un producto tensorial que contenga el mismo índice más de dos veces es sintácticamente incorrecto. La función indices intenta tratar estas expresiones de una forma razonable; sin embargo, cuando se la obliga a manipular una expresión incorrecta puede tener un comportamiento imprevisto.

 -- Función: rename (EXPR)
 -- Función: rename (EXPR, COUNT)

     Devuelve una expresión equivalente a EXPR pero con los índices mudos de cada término elegidos del conjunto  [%1, %2,...] si el segundo argumento opcional se omite. En otro caso, los índices mudos son indexados empezando con el valor COUNT. Cada índice mudo en un producto será diferente. En el caso de las sumas, la función rename operará sobre cada término de la suma reinicializando el contador con cada término. De esta manera rename puede servir como simplificador tensorial. Además, los índices se ordenarán alfanuméricamente, si la variable allsym vale true, respecto de los índices covariantes y contravariantes dependiendo del valor de flipflag. Si  flipflag vale false, entonces los índices se renombrarán de acuerdo con el orden de los índices contravariantes. Si flipflag vale true, entonces los índices se renombrarán de acuerdo con el orden de los índices covariantes. Suele acontecer que el efecto combinado de los dos cambios de nombre reduzcan la expresión más de lo que que pueda reducir cualquiera de ellas por separado.

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) allsym:true;
          (%o2)                                true
          (%i3) g([],[%4,%5])*g([],[%6,%7])*ichr2([%1,%4],[%3])*
          ichr2([%2,%3],[u])*ichr2([%5,%6],[%1])*ichr2([%7,r],[%2])-
          g([],[%4,%5])*g([],[%6,%7])*ichr2([%1,%2],[u])*
          ichr2([%3,%5],[%1])*ichr2([%4,%6],[%3])*ichr2([%7,r],[%2]),noeval$
          (%i4) expr:ishow(%)$

                 %4 %5  %6 %7      %3         u          %1         %2
          (%t4) g      g      ichr2      ichr2      ichr2      ichr2
                                   %1 %4      %2 %3      %5 %6      %7 r

                  %4 %5  %6 %7      u          %1         %3         %2
               - g      g      ichr2      ichr2      ichr2      ichr2
                                    %1 %2      %3 %5      %4 %6      %7 r
          (%i5) flipflag:true;
          (%o5)                                true
          (%i6) ishow(rename(expr))$
                 %2 %5  %6 %7      %4         u          %1         %3
          (%t6) g      g      ichr2      ichr2      ichr2      ichr2
                                   %1 %2      %3 %4      %5 %6      %7 r

                  %4 %5  %6 %7      u          %1         %3         %2
               - g      g      ichr2      ichr2      ichr2      ichr2
                                    %1 %2      %3 %4      %5 %6      %7 r
          (%i7) flipflag:false;
          (%o7)                                false
          (%i8) rename(%th(2));
          (%o8)                                  0
          (%i9) ishow(rename(expr))$
                 %1 %2  %3 %4      %5         %6         %7        u
          (%t9) g      g      ichr2      ichr2      ichr2     ichr2
                                   %1 %6      %2 %3      %4 r      %5 %7

                  %1 %2  %3 %4      %6         %5         %7        u
               - g      g      ichr2      ichr2      ichr2     ichr2
                                    %1 %3      %2 %6      %4 r      %5 %7

 -- Variable opcional: flipflag
     Valor por defecto: false

     Si vale false los índices se renombrarán de acuerdo con el orden de los índices covariantes, si true se renombrarán de acuerdo con el orden de los índices covariantes.

     Si flipflag vale false, entonces rename construye una lista con los índices contravariantes según van apareciendo de izquierda a derecha; si vale true, entonces va formando la lista con los covariantes. Al primer índice mudo se le da el nombre %1, al siguiente %2, etc. Finalmente se hace la ordenación. Véase el ejemplo en la descripción de la función rename.

 -- Función: defcon (TENSOR_1)
 -- Función: defcon (TENSOR_1, TENSOR_2, TENSOR_3)

     Le asigna a gives TENSOR_1 la propiedad de que la contracción de un producto de TENSOR_1 por TENSOR_2 da como resultado un TENSOR_3 con los índices apropiados. Si sólo se aporta un argumento, TENSOR_1, entonces la contracción del producto de TENSOR_1 por cualquier otro objeto indexado que tenga los índices apropiados, por ejemplo my_tensor, dará como resultado un objeto indexado con ese nombre, my_tensor, y con un nuevo conjunto de índices que reflejen las contracciones realizadas. Por ejemplo, si imetric:g, entonces defcon(g) implementará el aumento o disminución de los índices a través de la contracción con el tensor métrico. Se puede dar más de un defcon para el mismo objeto indexado, aplicándose el último. La variable contractions es una lista con aquellos objetos indexados a los que se le han dado propiedades de contracción con  defcon.

 -- Función: remcon (TENSOR_1, ..., TENSOR_N)
 -- Función: remcon (all)

     Borra todas las propiedades de contracción de TENSOR_1, ..., TENSOR_N). La llamada remcon(all) borra todas las propiedades de contracción de todos los objetos indexados.

 -- Función: contract (EXPR)

     Lleva a cabo las contracciones tensoriales en EXPR, la cual puede ser cualquier combinación de sumas y productos. Esta función utiliza la información dada a la función defcon. Para obtener mejores resultados, expr debería estar completamente expandida. La función ratexpand es la forma más rápida de expandir productos y potencias de sumas si no hay variables en los denominadores de los términos.

 -- Función: indexed_tensor (TENSOR)

     Debe ejecutarse antes de asignarle componentes a un TENSOR para el que ya existe un valor, como ichr1, ichr2 o icurvature. Véase el ejemplo de la descripción de icurvature.

 -- Función: components (TENSOR, EXPR)

     Permite asignar un valor indexado a la expresión EXPR dando los valores de las componentes de TENSOR. El tensor debe ser de la forma t([...],[...]), donde cualquiera de las listas puede estar vacía. La expresión  EXPR puede ser cualquier objeto indexado que tenga otros objetos con los mismos índices libres que TENSOR. Cuando se utiliza para asignar valores al tensor métrico en el que las componentes contengan índices mudos, se debe tener cuidado en no generar índices mudos múltiples. Se pueden borrar estas asignaciones con la función  remcomps.

     Es importante tener en cuenta que components controla la valencia del tensor, no el orden de los índices. Así, asignando componentes de la forma x([i,-j],[]), x([-j,i],[]) o x([i],[j]) todos ellos producen el mismo resultado, la asignación de componentes a un tensor de nombre x con valencia (1,1).

     Las componentes se pueden asignar a una expresión indexada de cuatro maneras, dos de las cuales implican el uso de la instrucción components:

     1) Como una expresión indexada. Por ejemplo:

          (%i2) components(g([],[i,j]),e([],[i])*p([],[j]))$
          (%i3) ishow(g([],[i,j]))$
                                                i  j
          (%t3)                                e  p

     2) Como una matriz:

          (%i6) components(g([i,j],[]),lg);
          (%o6)                                done
          (%i7) ishow(g([i,j],[]))$
          (%t7)                                g
                                                i j
          (%i8) g([3,3],[]);
          (%o8)                                  1
          (%i9) g([4,4],[]);
          (%o9)                                 - 1

     3) Como una función. Se puede utilizar una función de Maxima para especificar las componentes de un tensor en base a sus índices. Por ejemplo, el código siguiente asigna  kdelta a h si h tiene el mismo número de índices covariantes y contravariantes y no tiene índices de derivadas, asignándole g en otro caso:

          (%i4) h(l1,l2,[l3]):=if length(l1)=length(l2) and length(l3)=0
            then kdelta(l1,l2) else apply(g,append([l1,l2], l3))$
          (%i5) ishow(h([i],[j]))$
                                                    j
          (%t5)                               kdelta
                                                    i
          (%i6) ishow(h([i,j],[k],l))$
                                               k
          (%t6)                               g
                                               i j,l

     4) Utilizando los patrones de Maxima, en particular las funciones defrule y applyb1:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) matchdeclare(l1,listp);
          (%o2)                                done
          (%i3) defrule(r1,m(l1,[]),(i1:idummy(),
                g([l1[1],l1[2]],[])*q([i1],[])*e([],[i1])))$

          (%i4) defrule(r2,m([],l1),(i1:idummy(),
                w([],[l1[1],l1[2]])*e([i1],[])*q([],[i1])))$

          (%i5) ishow(m([i,n],[])*m([],[i,m]))$
                                              i m
          (%t5)                              m    m
                                                   i n
          (%i6) ishow(rename(applyb1(%,r1,r2)))$
                                     %1  %2  %3 m
          (%t6)                     e   q   w     q   e   g
                                                   %1  %2  %3 n

 -- Función: remcomps (TENSOR)

     Borra todos los valores de TENSOR que han sido asignados con la función components.

 -- Función: showcomps (TENSOR)

     Muestra las componentes de un tensor definidas con la instrucción components. Por ejemplo:

          (%i1) load(ctensor);
          (%o1)       /share/tensor/ctensor.mac
          (%i2) load(itensor);
          (%o2)      /share/tensor/itensor.lisp
          (%i3) lg:matrix([sqrt(r/(r-2*m)),0,0,0],[0,r,0,0],
                          [0,0,sin(theta)*r,0],[0,0,0,sqrt((r-2*m)/r)]);
                         [         r                                     ]
                         [ sqrt(-------)  0       0              0       ]
                         [      r - 2 m                                  ]
                         [                                               ]
                         [       0        r       0              0       ]
          (%o3)          [                                               ]
                         [       0        0  r sin(theta)        0       ]
                         [                                               ]
                         [                                      r - 2 m  ]
                         [       0        0       0        sqrt(-------) ]
                         [                                         r     ]
          (%i4) components(g([i,j],[]),lg);
          (%o4)                                done
          (%i5) showcomps(g([i,j],[]));
                            [         r                                     ]
                            [ sqrt(-------)  0       0              0       ]
                            [      r - 2 m                                  ]
                            [                                               ]
                            [       0        r       0              0       ]
          (%t5)      g    = [                                               ]
                      i j   [       0        0  r sin(theta)        0       ]
                            [                                               ]
                            [                                      r - 2 m  ]
                            [       0        0       0        sqrt(-------) ]
                            [                                         r     ]
          (%o5)                                false

     La función showcomps también puede mostrar las componentes de tensores de rango mayor de 2.

 -- Función: idummy ()

     Incrementa icounter y devuelve un índice de la forma %n siendo n un entero positivo.  Esto garantiza que índices mudos que sean necesarios para formar expresiones no entren en conflico con índices que ya están en uso. Véase el ejemplo de la descripción de indices.

 -- Variable opcional: idummyx
     Valor por defecto: %

     Es el prefijo de los índices mudos. Véase indices.

 -- Variable opcional: icounter
     Valor por defecto: 1

     Determina el sufijo numérico a ser utilizado en la generación del siguiente índice mudo. El prefijo se determina con la opción idummy (por defecto: %).

 -- Función: kdelta (L1, L2)

     Es la función delta generalizada de Kronecker definida en el paquete itensor siendo L1 la lista de índices covariantes y L2 la lista de índices contravariantes. La función  kdelta([i],[j]) devuelve el valor de la delta ordinaria de Kronecker. La instrucción ev(EXPR,kdelta) provoca la evaluación de una expresión que contenga kdelta([],[]).

     En un abuso de la notación, itensor también permite a kdelta tener 2 índices covariantes y ninguno contravariante, o 2 contravariantes y ninguno covariante. Esto es una funcionalidad del paquete, loque no implica que  kdelta([i,j],[]) sea un objeto tensorial de pleno derecho.

 -- Función: kdels (L1, L2)

     Función delta de Kronecker simetrizada, utilizada en algunos cálculos. Por ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) kdelta([1,2],[2,1]);
          (%o2)                                 - 1
          (%i3) kdels([1,2],[2,1]);
          (%o3)                                  1
          (%i4) ishow(kdelta([a,b],[c,d]))$
                                       c       d         d       c
          (%t4)                  kdelta  kdelta  - kdelta  kdelta
                                       a       b         a       b
          (%i4) ishow(kdels([a,b],[c,d]))$
                                       c       d         d       c
          (%t4)                  kdelta  kdelta  + kdelta  kdelta
                                       a       b         a       b

 -- Función: levi_civita (L)

     Es el tensor de permutación de Levi-Civita, el cual devuelve 1  si la lista L con una permutación par de enteros, -1 si es en una permutación impar y 0 si algunos de los índices de L están repetidos.

 -- Función: lc2kdt (EXPR)

     Simplifica expresiones que contengan el símbolo de Levi-Civita, convirtiéndolas en expresiones con la delta de Kronecker siempre que sea posible. La diferencia principal entre esta función y la simple evaluación del símbolo de Levi-Civita consiste en que de esta última forma se obtienen expresiones de Kronecker con índices numéricos, lo que impide simplificaciones ulteriores. La función lc2kdt evita este problema, dando resultados con son más fáciles de simplificar con rename o contract.

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) expr:ishow('levi_civita([],[i,j])
                           *'levi_civita([k,l],[])*a([j],[k]))$
                                            i j  k
          (%t2)                  levi_civita    a  levi_civita
                                                 j            k l
          (%i3) ishow(ev(expr,levi_civita))$
                                            i j  k       1 2
          (%t3)                       kdelta    a  kdelta
                                            1 2  j       k l
          (%i4) ishow(ev(%,kdelta))$
                       i       j         j       i   k
          (%t4) (kdelta  kdelta  - kdelta  kdelta ) a
                       1       2         1       2   j

                                         1       2         2       1
                                  (kdelta  kdelta  - kdelta  kdelta )
                                         k       l         k       l
          (%i5) ishow(lc2kdt(expr))$
                               k       i       j    k       j       i
          (%t5)               a  kdelta  kdelta  - a  kdelta  kdelta
                               j       k       l    j       k       l
          (%i6) ishow(contract(expand(%)))$
                                           i           i
          (%t6)                           a  - a kdelta
                                           l           l

     La función lc2kdt en ocasiones hace uso del tensor métrico. Si el tensor métrico no fue previamente definido con imetric, se obtiene un mensaje de error.

          (%i7) expr:ishow('levi_civita([],[i,j])
                           *'levi_civita([],[k,l])*a([j,k],[]))$
                                           i j            k l
          (%t7)                 levi_civita    levi_civita    a
                                                               j k
          (%i8) ishow(lc2kdt(expr))$
          Maxima encountered a Lisp error:

           Error in $IMETRIC [or a callee]:
           $IMETRIC [or a callee] requires less than two arguments.

          Automatically continuing.
          To reenable the Lisp debugger set *debugger-hook* to nil.
          (%i9) imetric(g);
          (%o9)                                done
          (%i10) ishow(lc2kdt(expr))$
                   %3 i       k   %4 j       l     %3 i       l   %4 j
          (%t10) (g     kdelta   g     kdelta   - g     kdelta   g
                              %3             %4               %3
                        k
                  kdelta  ) a
                        %4   j k
          (%i11) ishow(contract(expand(%)))$
                                            l i      l i
          (%t11)                           a    - a g

 -- Función: lc_l

     Regla de simplificación utilizada en expresiones que contienen el símbolo de levi_civita sin evaluar. Junto con  lc_u, puede utilizarse para simplificar muchas expresiones de forma más eficiente que la evaluación de levi_civita. Por ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2)  el1:ishow('levi_civita([i,j,k],[])*a([],[i])*a([],[j]))$
                                       i  j
          (%t2)                       a  a  levi_civita
                                                       i j k
          (%i3) el2:ishow('levi_civita([],[i,j,k])*a([i])*a([j]))$
                                                 i j k
          (%t3)                       levi_civita      a  a
                                                        i  j
          (%i4) ishow(canform(contract(expand(applyb1(el1,lc_l,lc_u)))))$
          (%t4)                                  0
          (%i5) ishow(canform(contract(expand(applyb1(el2,lc_l,lc_u)))))$
          (%t5)                                  0

 -- Función: lc_u

     Regla de simplificación utilizada en expresiones que contienen el símbolo de levi_civita sin evaluar. Junto con  lc_l, puede utilizarse para simplificar muchas expresiones de forma más eficiente que la evaluación de levi_civita. Véase lc_l.

 -- Función: canten (EXPR)

     Simplifica EXPR renombrando (véase rename) y permutando índices mudos. La función rename se restringe a sumas de productos de tensores en los cuales no hay derivadas, por lo que está limitada y sólo debería utilizarse si canform no es capaz de de llevar a cabo la simplificación requerida.

     La función canten devuelve un resultado matemáticamente correcto sólo si su argumento es una expresión completamente simétrica respecto de sus índices. Por esta razón, canten devuelve un error si allsym no vale true.

 -- Función: concan (EXPR)

     Similar a canten pero también realiza la contracción de los índices.

Simetrías de tensores
---------------------

 -- Variable opcional: allsym
     Valor por defecto: false

     Si vale true entonces todos los objetos indexados se consideran simétricos respecto de todos sus índices covariantes y contravariantes. Si vale false entonces no se tienen en cuenta ningún tipo de simetría para estos índices. Los índices de derivadas se consideran siempre simétricos, a menos que la variable iframe_flag valga true.

 -- Función: decsym (TENSOR, M, N, [COV_1, COV_2, ...], [CONTR_1, CONTR_2, ...])

     Declara propiedades de simetría para el TENSOR de M índices covariantes y N contravariantes. Los COV_I y CONTR_I son seudofunciones que expresan relaciones de simetría entre los índices covariantes y contravariantes, respectivamente. Éstos son de la forma symoper(INDEX_1, INDEX_2,...) donde symoper es uno de sym, anti o cyc y los INDEX_I son enteros que indican la posición del índice en el TENSOR.  Esto declarará a TENSOR simétrico, antisimétrico o cíclico respecto de INDEX_I. La llamada symoper(all) indica que todos los índices cumplen la condición de simetría. Por ejemplo, dado un objeto b con 5 índices covariantes, decsym(b,5,3,[sym(1,2),anti(3,4)],[cyc(all)]) declara b simétrico en el primer y segundo índices covariantes, antisimétrico en su tercer y cuarto índices también covariantes y cíclico en todos sus índices contravariantes. Cualquiera de las listas de declaración de simetrías puede ser nula. La función que realiza las simplificaciones es canform, como se ilustra en el siguiente ejemplo,

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) expr:contract(expand(a([i1,j1,k1],[])
                      *kdels([i,j,k],[i1,j1,k1])))$
          (%i3) ishow(expr)$
          (%t3)         a      + a      + a      + a      + a      + a
                         k j i    k i j    j k i    j i k    i k j    i j k
          (%i4) decsym(a,3,0,[sym(all)],[]);
          (%o4)                                done
          (%i5) ishow(canform(expr))$
          (%t5)                              6 a
                                                i j k
          (%i6) remsym(a,3,0);
          (%o6)                                done
          (%i7) decsym(a,3,0,[anti(all)],[]);
          (%o7)                                done
          (%i8) ishow(canform(expr))$
          (%t8)                                  0
          (%i9) remsym(a,3,0);
          (%o9)                                done
          (%i10) decsym(a,3,0,[cyc(all)],[]);
          (%o10)                               done
          (%i11) ishow(canform(expr))$
          (%t11)                        3 a      + 3 a
                                           i k j      i j k
          (%i12) dispsym(a,3,0);
          (%o12)                     [[cyc, [[1, 2, 3]], []]]

 -- Función: remsym (TENSOR, M, N)

     Borra todas las propiedades de simetría del TENSOR que tiene M índices covariantes y N contravariantes.

 -- Función: canform (EXPR)

     Simplifica EXPR renombrando índices mudos y reordenando todos los índices según las condiciones de simetría que se le hayan impuesto. Si allsym vale true entonces todos los índices se consideran simétricos, en otro caso se utilizará la información sobre simetrías suministrada por decsym. Los índices mudos se renombran de la misma manera que en la función rename. Cuando canform se aplica a una expresión grande el cálculo puede llevar mucho tiempo. Este tiempo se puede acortar llamando primero  a rename. Véase también el ejemplo de la descripción de decsym. La función canform puede que no reduzca completamente una expresión a su forma más sencilla, pero en todo caso devolverá un resultado matemáticamente correcto.

Cálculo tensorial indexado
--------------------------

 -- Función: diff (EXPR, V_1, [N_1, [V_2, N_2] ...])

     Se trata de la función de Maxima para la diferenciación, ampliada para las necesidades del paquete itensor. Calcula la derivada de EXPR respecto de V_1 N_1 veces, respecto de V_2 N_2 veces, etc. Para el paquete de tensores,la función ha sido modificada de manera que V_I puedan ser enteros desde 1 hasta el valor que tome la variable dim. Esto permite que la derivación se pueda realizar con respecto del V_I-ésimo miembro de la lista vect_coords. Si vect_coords guarda una variable atómica, entonces esa variable será la que se utilice en la derivación. Con esto se hace posible la utilización de una lista con nombres de coordenadas subindicadas, como x[1], x[2], ...

 -- Función: idiff (EXPR, V_1, [N_1, [V_2, N_2] ...])
     Diferenciación inicial. Al contrario que diff, que deriva respecto de una variable independiente, idiff puede usarse para derivar respecto de una coordenada.

     La función idiff también puede derivar el determinante del tensor métrico. Así, si imetric toma el valor G entonces idiff(determinant(g),k) devolverá 2*determinant(g)*ichr2([%i,k],[%i]) donde la índice mudo %i se escoge de forma apropiada.

 -- Función: liediff (V, TEN)

     Calcula la derivada de Lie de la expresión tensorial TEN respecto de campo vectorial V. La expresión TEN debe ser cualquier tensor indexado; V debe ser el nombre (sin índices) de un campo vectorial. Por ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) ishow(liediff(v,a([i,j],[])*b([],[k],l)))$
                 k    %2            %2          %2
          (%t2) b   (v   a       + v   a     + v   a    )
                 ,l       i j,%2    ,j  i %2    ,i  %2 j

                                    %1  k        %1  k      %1  k
                                + (v   b      - b   v    + v   b   ) a
                                        ,%1 l    ,l  ,%1    ,l  ,%1   i j

 -- Función: rediff (TEN)

     Calcula todas las instrucciones idiff que aparezcan en la expresión tensorial TEN.

 -- Función: undiff (EXPR)

     Devuelve una expresión equivalente a EXPR pero con todas las derivadas de los objetos indexados reemplazadas por la forma nominal de la función idiff.

 -- Función: evundiff (EXPR)

     Equivale a undiff seguido de  ev y rediff.

     La razón de esta operación es evaluar de forma sencilla expresiones que no pueden ser directamente evaluadas en su forma derivada. Por ejemplo, lo siguiente provoca un error:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) icurvature([i,j,k],[l],m);
          Maxima encountered a Lisp error:

           Error in $ICURVATURE [or a callee]:
           $ICURVATURE [or a callee] requires less than three arguments.

          Automatically continuing.
          To reenable the Lisp debugger set *debugger-hook* to nil.

     Sin embargo, si icurvature se da en forma nominal, puede ser evaluada utilizando evundiff:

          (%i3) ishow('icurvature([i,j,k],[l],m))$
                                                   l
          (%t3)                          icurvature
                                                   i j k,m
          (%i4) ishow(evundiff(%))$
                       l              l         %1           l           %1
          (%t4) - ichr2        - ichr2     ichr2      - ichr2       ichr2
                       i k,j m        %1 j      i k,m        %1 j,m      i k

                       l              l         %1           l           %1
                + ichr2        + ichr2     ichr2      + ichr2       ichr2
                       i j,k m        %1 k      i j,m        %1 k,m      i j

     Nota: en versiones antiguas de Maxima, las formas derivadas de los símbolos de Christoffel no se podían evaluar. Este fallo ha sido subsanado, de manera que evundiff ya no se necesita en expresiones como esta:

          (%i5) imetric(g);
          (%o5)                                done
          (%i6) ishow(ichr2([i,j],[k],l))$
                 k %3
                g     (g         - g         + g        )
                        j %3,i l    i j,%3 l    i %3,j l
          (%t6) -----------------------------------------
                                    2

                                   k %3
                                  g     (g       - g       + g      )
                                   ,l     j %3,i    i j,%3    i %3,j
                                + -----------------------------------
                                                   2

 -- Función: flush (EXPR, TENSOR_1, TENSOR_2, ...)

     Iguala a cero en la expresión EXPR todas las apariciones de TENSOR_I que no tengan índices de derivadas.

 -- Función: flushd (EXPR, TENSOR_1, TENSOR_2, ...)

     Iguala a cero en la expresión EXPR todas las apariciones de TENSOR_I que tengan índices de derivadas

 -- Función: flushnd (EXPR, TENSOR, N)

     Iguala a cero en EXPR todas las apariciones del objeto diferenciado  TENSOR que tenga N o más índices de derivadas, como demuestra el siguiente ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) ishow(a([i],[J,r],k,r)+a([i],[j,r,s],k,r,s))$
                                          J r      j r s
          (%t2)                          a      + a
                                          i,k r    i,k r s
          (%i3) ishow(flushnd(%,a,3))$
                                               J r
          (%t3)                               a
                                               i,k r

 -- Función: coord (TENSOR_1, TENSOR_2, ...)

     Le da a TENSOR_I la propiedad de diferenciación coordenada, que la derivada del vector contravariante cuyo nombre es uno de los TENSOR_I es igual a la delta de Kronecker.  Por ejemplo, si se ha hecho coord(x) entonces  idiff(x([],[i]),j) da kdelta([i],[j]). La llamada coord devuelve una lista de todos los objetos indexados con esta propiedad.

 -- Función: remcoord (TENSOR_1, TENSOR_2, ...)
 -- Función: remcoord (all)

     Borra todas las propiedades de diferenciación coordenada de tensor_i que hayan sido establecidas por la función coord. La llamada remcoord(all) borra esta propiedad de todos los objetos indexados.

 -- Función: makebox (EXPR)
     Muestra EXPR de la misma manera que lo hace show; sin embargo, cualquier tensor de d'Alembert que aparezca en  EXPR estará indicado por [].  Por ejemplo, []p([m],[n]) representa g([],[i,j])*p([m],[n],i,j).

 -- Función: conmetderiv (EXPR, TENSOR)

     Simplifica expresiones que contengan derivadas ordinarias tanto de las formas covariantes como contravariantes del tensor métrico. Por ejemplo,  conmetderiv puede relacionar la derivada del tensor métrico contravariante con los símbolos de Christoffel, como se ve en el ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) ishow(g([],[a,b],c))$
                                                a b
          (%t2)                                g
                                                ,c
          (%i3) ishow(conmetderiv(%,g))$
                                   %1 b      a       %1 a      b
          (%t3)                 - g     ichr2     - g     ichr2
                                             %1 c              %1 c

 -- Función: simpmetderiv (EXPR)
 -- Función: simpmetderiv (EXPR[, STOP])

     Simplifica expresiones que contienen productos de las derivadas del tensor métrico. La función simpmetderiv reconoce dos identidades:

             ab        ab           ab                 a
            g   g   + g   g     = (g   g  )   = (kdelta )   = 0
             ,d  bc        bc,d         bc ,d          c ,d

     de donde

             ab          ab
            g   g   = - g   g
             ,d  bc          bc,d

     y

            ab          ab
           g   g     = g   g
            ,j  ab,i    ,i  ab,j

     que se deduce de las simetrías de los símbolos de Christoffel.

     La función simpmetderiv tiene un argumento opcional, el cual detiene la función después de la primera sustitución exitosa en un expresión producto. La función simpmetderiv también hace uso de la variable global FLIPFLAG que determina cómo aplicar una ordenación ``canónica'' a los índices de los productos.

     Todo esto se puede utilizar para conseguir buenas simplificaciones que serían difíciles o imposibles de conseguir, lo que se demuestra en el siguiente ejemplo, que utiliza explícitamente las simplificaciones parciales de simpmetderiv:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) imetric(g);
          (%o2)                                done
          (%i3) ishow(g([],[a,b])*g([],[b,c])*g([a,b],[],d)*g([b,c],[],e))$
                                       a b  b c
          (%t3)                       g    g    g      g
                                                 a b,d  b c,e
          (%i4) ishow(canform(%))$

          errexp1 has improper indices
           -- an error.  Quitting.  To debug this try debugmode(true);
          (%i5) ishow(simpmetderiv(%))$
                                       a b  b c
          (%t5)                       g    g    g      g
                                                 a b,d  b c,e
          (%i6) flipflag:not flipflag;
          (%o6)                                true
          (%i7) ishow(simpmetderiv(%th(2)))$
                                         a b  b c
          (%t7)                         g    g    g    g
                                         ,d   ,e   a b  b c
          (%i8) flipflag:not flipflag;
          (%o8)                                false
          (%i9) ishow(simpmetderiv(%th(2),stop))$
                                         a b  b c
          (%t9)                       - g    g    g      g
                                              ,e   a b,d  b c
          (%i10) ishow(contract(%))$
                                              b c
          (%t10)                           - g    g
                                              ,e   c b,d

     Véase también weyl.dem para un ejemplo que utiliza simpmetderiv y conmetderiv para simplificar contracciones del tensor de Weyl.

 -- Función: flush1deriv (EXPR, TENSOR)

     Iguala a cero en expr todas las apariciones de tensor que tengan exactamente un índice derivado.

Tensores en espacios curvos
---------------------------

 -- Función: imetric (G)
 -- Variable de sistema: imetric
     Especifica la métrica haciendo la asignación de la variable imetric:G, además las propiedades de contracción de la métrica G se fijan ejecutando las instrucciones defcon(G), defcon(G,G,kdelta). La variable imetric, a la que no se le asigna ningún valor por defecto, tiene el valor de la métrica que se le haya asignado con la instrucción imetric(G).

 -- Función: idim (N)
     Establece las dimensiones de la métrica. También inicializa las propiedades de antisimetría de los símbolos de Levi-Civita para la dimensión dada.

 -- Función: ichr1 ([I, J, K])
     Devuelve el símbolo de Christoffel de primera especie dado por la definición

                 (g      + g      - g     )/2 .
                   ik,j     jk,i     ij,k

     Para evaluar los símbolos de Christoffel de una métrica determinada, a la variable  imetric hay que asignarle un nombre como en el ejemplo de la descripción de chr2.

 -- Función: ichr2 ([I, J], [K])
     Devuelve el símbolo de Christoffel de segunda especie dado por la definición

                                 ks
             ichr2([i,j],[k]) = g    (g      + g      - g     )/2
                                       is,j     js,i     ij,s

 -- Función: icurvature ([I, J, K], [H])
     Devuelve el tensor de curvatura de Riemann en términos de los símbolos de Christoffel de segunda especie (ichr2).  Se utiliza la siguiente notación:

                         h             h            h         %1         h
               icurvature     = - ichr2      - ichr2     ichr2    + ichr2
                         i j k         i k,j        %1 j      i k        i j,k
                                         h          %1
                                  + ichr2      ichr2
                                         %1 k       i j

 -- Función: covdiff (EXPR, V_1, V_2, ...)
     Devuelve la derivada covariante de EXPR respecto de las variables  V_I en términos de los símbolos de Christoffel de segunda especie (ichr2). Para evaluarlos debe hacerse ev(EXPR,ichr2).

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) entertensor()$
          Enter tensor name: a;
          Enter a list of the covariant indices: [i,j];
          Enter a list of the contravariant indices: [k];
          Enter a list of the derivative indices: [];
                                                k
          (%t2)                                a
                                                i j
          (%i3) ishow(covdiff(%,s))$
                       k         %1     k         %1     k
          (%t3)     - a     ichr2    - a     ichr2    + a
                       i %1      j s    %1 j      i s    i j,s

                       k     %1
                + ichr2     a
                       %1 s  i j
          (%i4) imetric:g;
          (%o4)                                  g
          (%i5) ishow(ev(%th(2),ichr2))$
                   %1 %4  k
                  g      a     (g       - g       + g      )
                          i %1   s %4,j    j s,%4    j %4,s
          (%t5) - ------------------------------------------
                                      2
              %1 %3  k
             g      a     (g       - g       + g      )
                     %1 j   s %3,i    i s,%3    i %3,s
           - ------------------------------------------
                                 2
              k %2  %1
             g     a    (g        - g        + g       )
                    i j   s %2,%1    %1 s,%2    %1 %2,s     k
           + ------------------------------------------- + a
                                  2                         i j,s

 -- Función: lorentz_gauge (EXPR)
     Impone la condición de Lorentz sustituyendo por 0 todos los objetos indexados de EXPR que tengan un índice derivado idéntico a un índice contravariante.

 -- Función: igeodesic_coords (EXPR, NOMBRE)

     Elimina los símbolos no diferenciados de Christoffel y las primeras derivadas del tensor métrico de EXPR. El argumento NOMBRE de la función igeodesic_coords se refiere a la métrica NOMBRE si aparece en EXPR, mientras que los coeficientes de conexión deben tener los nombres  ichr1 y/o ichr2. El siguiente ejemplo hace la verificación de la identidad cíclica satisfecha por el tensor de curvatura de Riemann haciendo uso de la función igeodesic_coords.

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) ishow(icurvature([r,s,t],[u]))$
                       u            u         %1         u
          (%t2) - ichr2      - ichr2     ichr2    + ichr2
                       r t,s        %1 s      r t        r s,t

                                                        u         %1
                                                 + ichr2     ichr2
                                                        %1 t      r s
          (%i3) ishow(igeodesic_coords(%,ichr2))$
                                           u            u
          (%t3)                       ichr2      - ichr2
                                           r s,t        r t,s
          (%i4) ishow(igeodesic_coords(icurvature([r,s,t],[u]),ichr2)+
                      igeodesic_coords(icurvature([s,t,r],[u]),ichr2)+
                      igeodesic_coords(icurvature([t,r,s],[u]),ichr2))$
                       u            u            u            u
          (%t4) - ichr2      + ichr2      + ichr2      - ichr2
                       t s,r        t r,s        s t,r        s r,t

                                                       u            u
                                                - ichr2      + ichr2
                                                       r t,s        r s,t
          (%i5) canform(%);
          (%o5)                                  0

Sistemas de referencia móviles
------------------------------

Maxima puede hacer cálculos utilizando sistemas de referencia móviles, los cuales pueden ser ortonormales o cualesquiera otros.

Para utilizar sistemas de referencia, primero se debe asignar a la variable iframe_flag el valor true. Con esto se hace que los símbolos de Christoffel, ichr1 y ichr2, sean reemplazados por los coeficientes icc1 y icc2 en los cálculos, cambiando así el comportamiento de covdiff y icurvature.

El sistema de referencia se define con dos tensores: el campo del sistema de referencia inverso (ifri, la base dual tetrad) y la métrica del sistema de referencia ifg. La métrica del sistema de referencia es la matriz identidad en los sistemas de referencia ortonormales, o la métrica de Lorentz en sistemas de referencia ortonormales en el espacio-tiempo de Minkowski.  El campo del sistema de referencia inverso define la base del sistema de referencia con vectores unitarios. Las propiedades contractivas se definen para el campo y la métrica del sistema de referencia.

Si iframe_flag vale true, muchas expresiones de itensor utilizan la métrica ifg en lugar de la métrica definida por imetric para incrementar y reducir índices.

IMPORTANTE: Asignando a la variable iframe_flag el valor true NO deshace las propiedades contractivas de una métrica establecidas con una llamada a defcon o a imetric. Si se utiliza el campo del sistema de referencia, es mejor definir la métrica asignando su nombre a la variable  imetric y NO hacer una llamada a la función imetric.

Maxima utiliza estos dos tensores para definir los coeficientes del sistema de referencia: ifc1 y and ifc2, los cuales forman parte de los coeficientes de conexión icc1 y icc2, tal como demuestra el siguiente ejemplo:

     (%i1) load(itensor);
     (%o1)      /share/tensor/itensor.lisp
     (%i2) iframe_flag:true;
     (%o2)                                true
     (%i3) ishow(covdiff(v([],[i]),j))$
                                    i        i     %1
     (%t3)                         v   + icc2     v
                                    ,j       %1 j
     (%i4) ishow(ev(%,icc2))$
                             %1      i           i        i
     (%t4)                  v   (ifc2     + ichr2    ) + v
                                     %1 j        %1 j     ,j
     (%i5) ishow(ev(%,ifc2))$
                 %1    i %2
                v   ifg     (ifb        - ifb        + ifb       )
                                j %2 %1      %2 %1 j      %1 j %2     i
     (%t5)      -------------------------------------------------- + v
                                        2                             ,j
     (%i6) ishow(ifb([a,b,c]))$
                            %5    %4
     (%t6)               ifr   ifr   (ifri        - ifri       )
                            a     b       c %4,%5       c %5,%4

Se utiliza un método alternativo  para calcular el sistema de referencia ifb si la variable iframe_bracket_form vale false:

     (%i8) block([iframe_bracket_form:false],ishow(ifb([a,b,c])))$
                            %7    %6        %6      %7
     (%t8)              (ifr   ifr     - ifr     ifr  ) ifri
                            a     b,%7      a,%7    b       c %6

 -- Variable: ifb

     Es el sistema de referencia soporte. La contribución de la métrica del campo a los coeficientes de conexión se expresa utilizando:

                    - ifb      + ifb      + ifb
                         c a b      b c a      a b c
          ifc1    = --------------------------------
              abc                  2

     El sistema de referencia soporte se define en términos del campo y la métrica del sistema de referencia. Se utilizan dos métodos alternativos dependiendo del valor de frame_bracket_form. Si vale true (que es el valor por defecto) o si itorsion_flag vale true:

                    d      e                                      f
          ifb =  ifr    ifr   (ifri      - ifri      - ifri    itr   )
             abc    b      c       a d,e       a e,d       a f    d e

     En otro caso:

                       e      d        d      e
          ifb    = (ifr    ifr    - ifr    ifr   ) ifri
             abc       b      c,e      b,e    c        a d

 -- Variable: icc1

     Coeficientes de conexión de primera especie. Se definen en itensor como

          icc1    = ichr1    - ikt1    - inmc1
              abc        abc       abc        abc

     En esta expresión, si iframe_flag vale true, el símbolo de Christoffel ichr1 se reemplaza por el coeficiente de conexión del sistema de referencia ifc1. Si itorsion_flag vale false, ikt1 será omitido. También se omite si se utiliza una base, ya que la torsión ya está calculada como parte del sistema de referencia.

 -- Variable: icc2

     Coeficientes de conexión de segunda especie. Se definen en itensor como

              c         c        c         c
          icc2   = ichr2   - ikt2   - inmc2
              ab        ab       ab        ab

     En esta expresión, si la variable iframe_flag vale true, el símbolo de Christoffel ichr2 se reemplaza por el coeficiente de conexión del sistema de referencia ifc2. Si itorsion_flag vale false, ikt2 se omite. También se omite si se utiliza una base de referncia. Por último, si  inonmet_flag vale false, se omite inmc2.

 -- Variable: ifc1

     Coeficiente del sistema de referencia de primera especie, también conocido como coeficientes de rotación de Ricci. Este tensor represnta la contribución de la métrica del sistema de referencia al coeficiente de conexión de primera especie, definido como

                    - ifb      + ifb      + ifb
                         c a b      b c a      a b c
          ifc1    = --------------------------------
              abc                   2

 -- Variable: ifc2

     Coeficiente del sistema de referencia de primera especie. Este tensor representa la contribución de la métrica del sistema de referencia al coeficiente de conexión de primera especie, definido como

              c       cd
          ifc2   = ifg   ifc1
              ab             abd

 -- Variable: ifr

     El campo del sistema de referencia. Se contrae con el campo inverso ifri para formar la métrica del sistema de referencia, ifg.

 -- Variable: ifri

     Campo inverso del sistema de referencia. Especifica la base del sistema de referencia (vectores de la base dual).

 -- Variable: ifg

     La métrica del sistema de referencia. Su valor por defecto es kdelta, pero puede cambiarse utilizando components.

 -- Variable: ifgi

     La métrica inversa del sistema de referencia. Se contrae con la métrica ifg para dar kdelta.

 -- Variable opcional: iframe_bracket_form
     Valor por defecto: true

     Especifica cómo se calcula ifb.

Torsión y no metricidad
-----------------------

Maxima trabaja con conceptos como la torsión y la no metricidad. Cuando la variable itorsion_flag vale true, la contribución de la torsión se añade a los coeficientes de conexión. También se añaden las componentes de no metricidad cuando inonmet_flag vale  true.

 -- Variable: inm

     Vector de no metricidad. La no metricidad conforme se define a partir de la derivada covariante del tensor métrico. La derivada covariante del tensor métrico, que normalmente es nula, se calcula, cuando inonmet_flag vale true, como

          g     =- g  inm
           ij;k     ij   k

 -- Variable: inmc1

     Permutación covariante de las componentes del vector de no metricidad. Se define como

                     g   inm  - inm  g   - g   inm
                      ab    c      a  bc    ac    b
          inmc1    = ------------------------------
               abc                 2

     (Sustitúyase g por ifg si se utiliza una métrica para el sistema de referencia.)

 -- Variable: inmc2

     Permutación contravariante de las componentes del vector de no metricidad. Se utiliza en los coeficientes de conexión si inonmet_flag vale true. Se define como

                                c         c         cd
                    -inm  kdelta  - kdelta  inm  + g   inm  g
               c        a       b         a    b          d  ab
          inmc2   = -------------------------------------------
               ab                        2

     (Sustitúyase g por ifg si se utiliza una métrica para el sistema de referencia.)

 -- Variable: ikt1

     Permutación covariante del tensor de permutación, también conocido como contorsión. Se define como

                            d           d       d
                    -g   itr  - g    itr   - itr   g
                      ad    cb    bd    ca      ab  cd
          ikt1    = ----------------------------------
              abc                   2

     (Sustitúyase g por ifg si se utiliza una métrica para el sistema de referencia.)

 -- Variable: ikt2

     Permutación contravariante del tensor de permutación, también conocido como contorsión. Se define como

              c     cd
          ikt2   = g   ikt1
              ab           abd

     (Sustitúyase g por ifg si se utiliza una métrica para el sistema de referencia.)

 -- Variable: itr

     Tensor de torsión. Para una métrica con torsión, la diferenciación covariante iterada de una función escalar no conmuta, tal como demuestra el siguiente ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) imetric:g;
          (%o2)                                  g
          (%i3) covdiff(covdiff(f([],[]),i),j)
                   -covdiff(covdiff(f([],[]),j),i)$
          (%i4) ishow(%)$
                                             %4              %2
          (%t4)                    f    ichr2    - f    ichr2
                                    ,%4      j i    ,%2      i j
          (%i5) canform(%);
          (%o5)                                  0
          (%i6) itorsion_flag:true;
          (%o6)                                true
          (%i7) covdiff(covdiff(f([],[]),i),j)
                  -covdiff(covdiff(f([],[]),j),i)$
          (%i8) ishow(%)$
                                     %8             %6
          (%t8)             f    icc2    - f    icc2    - f     + f
                             ,%8     j i    ,%6     i j    ,j i    ,i j
          (%i9) ishow(canform(%))$
                                             %1             %1
          (%t9)                     f    icc2    - f    icc2
                                     ,%1     j i    ,%1     i j
          (%i10) ishow(canform(ev(%,icc2)))$
                                             %1             %1
          (%t10)                    f    ikt2    - f    ikt2
                                     ,%1     i j    ,%1     j i
          (%i11) ishow(canform(ev(%,ikt2)))$
                                %2 %1                    %2 %1
          (%t11)          f    g      ikt1       - f    g      ikt1
                           ,%2            i j %1    ,%2            j i %1
          (%i12) ishow(factor(canform(rename(expand(ev(%,ikt1))))))$
                                     %3 %2            %1       %1
                               f    g      g      (itr    - itr   )
                                ,%3         %2 %1     j i      i j
          (%t12)               ------------------------------------
                                                2
          (%i13) decsym(itr,2,1,[anti(all)],[]);
          (%o13)                               done
          (%i14) defcon(g,g,kdelta);
          (%o14)                               done
          (%i15) subst(g,nounify(g),%th(3))$
          (%i16) ishow(canform(contract(%)))$
                                                     %1
          (%t16)                           - f    itr
                                              ,%1    i j

Álgebra exterior
----------------

Con el paquete itensor se pueden realizar operaciones en campos tensoriales covariantes antisimétricos. Un campo tensorial totalmente antisimétrrico de rango (0,L) se corresponde con una L-forma diferencial. Sobre estos objetos se define una operación que se llama producto exterior.

Desafortunadamente no hay consenso entre los autores a la hora de definir el producto exterior. Algunos autores prefieren una definición que se corresponde con la noción de antisimetrización, con lo que el producto externo de dos campos vectoriales se definiría como

                 a a  - a a
                  i j    j i
      a  /\ a  = -----------
       i     j        2

De forma más general, el producto de una p-forma por una q-forma se definiría como

                            1     k1..kp l1..lq
     A       /\ B       = ------ D              A       B
      i1..ip     j1..jq   (p+q)!  i1..ip j1..jq  k1..kp  l1..lq

donde D es la delta de Kronecker.

Otros autores, sin embargo, prefieren una definición ``geométrica'' que se corresponde con la noción del elemento de volumen,

     a  /\ a  = a a  - a a
      i     j    i j    j i

y, en el caso general,

                            1    k1..kp l1..lq
     A       /\ B       = ----- D              A       B
      i1..ip     j1..jq   p! q!  i1..ip j1..jq  k1..kp  l1..lq

Puesto que itensor un paquete de álgebra tensorial, la primera de estas dos definiciones parece la más natural. Sin embargo, muchas aplicaciones hacen uso de la segunda definición. Para resolver el dilema, se define una variable que controla el comportamiento del producto exteriort: si igeowedge_flag vale false (el valor por defecto), se utiliza la primera definición, si vale true, la segunda.

 -- Operador: ~

     El operador del producto exterior se representa por el símbolo ~. Este es un operador binario. Sus argumentos deben ser expresiones que tengan escalares, tensores covariantes de rango uno o tensores covariantes de rango l que hayan sido declarados antisimétricos en todos los índices covariantes.

     El comportamiento del operador del producto exterior se controla con la variable igeowedge_flag, como en el ejemplo siguiente:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) ishow(a([i])~b([j]))$
                                           a  b  - b  a
                                            i  j    i  j
          (%t2)                            -------------
                                                 2
          (%i3) decsym(a,2,0,[anti(all)],[]);
          (%o3)                                done
          (%i4) ishow(a([i,j])~b([k]))$
                                    a    b  + b  a    - a    b
                                     i j  k    i  j k    i k  j
          (%t4)                     ---------------------------
                                                 3
          (%i5) igeowedge_flag:true;
          (%o5)                                true
          (%i6) ishow(a([i])~b([j]))$
          (%t6)                            a  b  - b  a
                                            i  j    i  j
          (%i7) ishow(a([i,j])~b([k]))$
          (%t7)                     a    b  + b  a    - a    b
                                     i j  k    i  j k    i k  j

 -- Operador: |

     La barra vertical | representa la operación "contracción con un vector". Cuando un tensor covariante totalmente antisimétrico se contrae con un vector contravariante, el resultado no depende del índice utilizado para la contracción. Así, es posible definir la operación de contracción de forma que no se haga referencia al índice.

     En el paquete itensor la contracción con un vector se realiza siempre respecto del primer índice de la ordenación literal. Ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) decsym(a,2,0,[anti(all)],[]);
          (%o2)                                done
          (%i3) ishow(a([i,j],[])|v)$
                                              %1
          (%t3)                              v   a
                                                  %1 j
          (%i4) ishow(a([j,i],[])|v)$
                                               %1
          (%t4)                             - v   a
                                                   %1 j

     Nótese que es primordial que los tensores utilizados junto con el operador | se declaren totalmente antisimétricos en sus  índices covariantes. De no ser así, se pueden obtener resultados incorrectos.

 -- Función: extdiff (EXPR, I)

     Calcula la derivada exterior de EXPR con respecto del índice I. La derivada exterior se define formalmente como el producto exterior del operador de la derivada parcial y una forma diferencial. Por lo tanto, esta operación también se ve afectada por el valor que tome la variable igeowedge_flag. Ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) ishow(extdiff(v([i]),j))$
                                            v    - v
                                             j,i    i,j
          (%t2)                             -----------
                                                 2
          (%i3) decsym(a,2,0,[anti(all)],[]);
          (%o3)                                done
          (%i4) ishow(extdiff(a([i,j]),k))$
                                     a      - a      + a
                                      j k,i    i k,j    i j,k
          (%t4)                      ------------------------
                                                3
          (%i5) igeowedge_flag:true;
          (%o5)                                true
          (%i6) ishow(extdiff(v([i]),j))$
          (%t6)                             v    - v
                                             j,i    i,j
          (%i7) ishow(extdiff(a([i,j]),k))$
          (%t7)                      a      - a      + a
                                      j k,i    i k,j    i j,k

 -- Función: hodge (EXPR)

     Calcula el dual de Hodge  EXPR. Por ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) imetric(g);
          (%o2)                            done
          (%i3) idim(4);
          (%o3)                            done
          (%i4) icounter:100;
          (%o4)                             100
          (%i5) decsym(A,3,0,[anti(all)],[])$

          (%i6) ishow(A([i,j,k],[]))$
          (%t6)                           A
                                           i j k
          (%i7) ishow(canform(hodge(%)))$
                                    %1 %2 %3 %4
                         levi_civita            g        A
                                                 %1 %102  %2 %3 %4
          (%t7)          -----------------------------------------
                                             6
          (%i8) ishow(canform(hodge(%)))$
                           %1 %2 %3 %8            %4 %5 %6 %7
          (%t8) levi_civita            levi_civita            g
                                                               %1 %106
                                       g        g        g      A         /6
                                        %2 %107  %3 %108  %4 %8  %5 %6 %7
          (%i9) lc2kdt(%)$

          (%i10) %,kdelta$

          (%i11) ishow(canform(contract(expand(%))))$
          (%t11)                     - A
                                        %106 %107 %108

 -- Variable opcional: igeowedge_flag
     Valor por defecto: false

     Controla el comportamiento del producto exterior y de la derivada exterior. Cuando vale false, la noción de formas diferenciales se corresponde con el de campo tensorial covariante totalmente antisimétrico. Cuando vale true, las formas diferenciales se corresponden con la idea de elemento de volumen.

Exportando expresiones en TeX
-----------------------------

El paquete itensor dispone de soporte limitado para exportar expresiones con tensores a TeX. Puesto que las expresiones de itensor son llamadas a funciones, puede que la instrucción habitual en Maxima, tex, no devuleva los resultados esperados. Se puede utlizar el comando tentex, que tratará de traducir expresiones tensoriales a objetos de TeX correctamente indexados.

 -- Función: tentex (EXPR)

     Para utilizar la función tentex, primero se debe cargar tentex, tal como muestra el siguiente ejemplo:

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) load(tentex);
          (%o2)       /share/tensor/tentex.lisp
          (%i3) idummyx:m;
          (%o3)                                  m
          (%i4) ishow(icurvature([j,k,l],[i]))$
                      m1       i           m1       i           i
          (%t4)  ichr2    ichr2     - ichr2    ichr2     - ichr2
                      j k      m1 l        j l      m1 k        j l,k

                                                                i
                                                         + ichr2
                                                                j k,l
          (%i5) tentex(%)$
          $$\Gamma_{j\,k}^{m_1}\,\Gamma_{l\,m_1}^{i}-\Gamma_{j\,l}^{m_1}\,
           \Gamma_{k\,m_1}^{i}-\Gamma_{j\,l,k}^{i}+\Gamma_{j\,k,l}^{i}$$

     Nótese la asignación de la variable idummyx para evitar la aparición del símbolo del porcentaje en la expresión en TeX, que puede dar errores de compilación.

     Téngase en cuenta que esta versión de la función tentex es experimental.

Interactuando con ctensor
-------------------------

El paquete itensor genera código Maxima que luego puede ser ejecutado en el contexto del paquete ctensor. La función que se encarga de esta tarea es ic_convert.

 -- Function: ic_convert (EQN)

     Convierte la ecuación EQN del entorno itensor a una sentencia de asignación de ctensor. Sumas implícitas sobre índices mudos se hacen explícitas mientras que objetos indexados se transforman en arreglos (los subíndices de los arreglos se ordenan poniendo primero los covariantes seguidos de los contravariantes. La derivada de un objeto indexado se reemplazará por por la forma nominal de diff tomada con respecto a ct_coords con el subíndice correspondiente al índice derivado. Los símbolos de Christoffel ichr1  ichr2 se traducen a lcs y mcs, respectivamente. Además, se añaden bucles do para la sumación de todos los índices libres, de manera que la sentencia traducida pueda ser evaluada haciendo simplemente ev. Los siguientes ejemplos muestran las funcionalidades de esta función.

          (%i1) load(itensor);
          (%o1)      /share/tensor/itensor.lisp
          (%i2) eqn:ishow(t([i,j],[k])=f([],[])*g([l,m],[])*a([],[m],j)
                *b([i],[l,k]))$
                                       k        m   l k
          (%t2)                       t    = f a   b    g
                                       i j      ,j  i    l m
          (%i3) ic_convert(eqn);
          (%o3) for i thru dim do (for j thru dim do (
                 for k thru dim do
                  t        : f sum(sum(diff(a , ct_coords ) b
                   i, j, k                   m           j   i, l, k

           g    , l, 1, dim), m, 1, dim)))
            l, m
          (%i4) imetric(g);
          (%o4)                                done
          (%i5) metricconvert:true;
          (%o5)                                true
          (%i6) ic_convert(eqn);
          (%o6) for i thru dim do (for j thru dim do (
                 for k thru dim do
                  t        : f sum(sum(diff(a , ct_coords ) b
                   i, j, k                   m           j   i, l, k

           lg    , l, 1, dim), m, 1, dim)))
             l, m

Palabras reservadas
-------------------

Las siguientes palabras son utilizadas por el paquete itensor internamente, por lo que no deberían ser modificadas por el usuario:

       Palabra    Comentarios
       ------------------------------------------
       indices2() Versión interna de indices()
       conti      Lista los índices contravariantes
       covi       Lista los índices covariantes
       deri       Lista los índices de derivadas
       name       Devuelve el nombre de un objeto indexado
       concan
       irpmon
       lc0
       _lc2kdt0
       _lcprod
       _extlc