Due to not having such a framework at hand, I used the following workaround, which allowed me to still shorten some computation time as compared to doing everything by hand. It boils down to having a set of conventions in place for how to write functions of arbitrarily many variables (or vectors or matrices of these). Here is a summary of the main ideas:
Choose a set of symbols which you will assign a special meaning in this formalism and will never use in your other definitions. In this example we pick: x,A,B,usedi,nexti,addi,i#
, where i#
stands for i1, i2, i3, ...
etc. x
will be a dummy variable allowing for proper differentiation. A
and B
will be free indices on matrices (or vectors) which are not summed over. i#
will be indices which are implicitly summed over (performing matrix multiplication). Therefore, in any expression any index i#
should appear either exactly twice or exactly zero times.
Make a clear distinction between simple numbers and functions. For numbers we can write the usual symbols in Mathematics, but functions should always be defined as
Superscript[#,0][x]
where the #
is a placeholder for the type of function you prefer. For instance, a simple function $f$ would be Superscript[f,0][x]
, and a vector of functions (with arbitrarily many components, simply denoted by the free index A
) will be Superscript[Subscript[v,A],0][x]
and so on (generalisation to matrices or tensors is straightforward).
Along with functions we should define a differentiation scheme. Consider defining:
d[f_,v_]:=D[f,x]/.Superscript[fun_,sup_]'[x]->Superscript[fun,sup+v][x];
Clearly, if we use d[#,#]
instead of D[#,#]
with arbitrary functions defined as described above, then we can take derivatives with respect to any variables we like. The superscript of a function is zero if it was differentiated zero times. Once differentiation is done, the variables in respect to which the differentiation happens will appear in the superscript, keeping track of the derivatives.
Finally, when we define composite expressions, which feature free indices as well as summed over indices, we make these quantities into functions of the free indices. For example, we could write:
result1[A_,B_] = Superscript[Subscript[M1, A, i1],0][x] Superscript[Subscript[M2, i1, B],0][x];
(Note how the index i1
appears exactly twice, since it is summed over). For example, we could also define:
result2[A_,B_] = Superscript[Subscript[M3, A, i2],0][x] Superscript[Subscript[M4, i2, B],0][x];
And then consider the matrix multiplication of both results:
result1[A,i3]result2[i3,B]
Or, even the derivative with respect to for example y
of this whole thing:
d[result1[A,i3]result2[i3,B],y]
Further usage should be intuitively clear at this point.
We should keep track of which indices i#
have already been used (they should not appear again in any other expression, which might get combined with i.e. result1[A_,B_]
, since it would lead to loss of information about which matrix is multiplied with which). If we feel like we could loose track of the i#
, it might be convenient to define the following list before we use any of the i#
usedi = {0};
Whenever we want to use an i#
in our equations or definitions, we can obtain the proper next number for #
by calling nexti
given by:
nexti:=Dimensions[usedi][[1]];
And once we use a certain amount of i#
s, lets say i1,i2,i3
, we should call addi[3]
given by
addi[y_] := Module[{}, usedi = ConstantArray[0, nexti + y];];
to append the same amount of zeros to usedi
as i#
s used, in order for nexti
to give us the correct number the next time we call it. This is more of a precaution, in practice I never lost track of my i#
s without making use of usedi
and the others.
With this the formalism is basically set up. Clearly, using the above it is straightforward to setup "submatrices" (quantities with indices written as subscripts) of actual matrices realised in Mathematica. Like for instance:
Mat[A_,B_] = {{Superscript[s11,0][x],Superscript[Subscript[v1tr,B],0][x]},{Superscript[Subscript[v2,A],0][x],Superscript[Subscript[m,A,B],0][x]}}
This whole business might not look particularly nice or wieldy, but the desired properties of having functions of arbitrary many variables and a submatrix structure is realised.