I've defined a primitive sort of type system where my objects are defined as lists with an associated identifier:
{scalarType, vectorType, bivectorType, trivectorType, multivectorType} = Range[5];
v = { vectorType, 3 PauliMatrix[2] }
b = { bivectorType, Sin[x] PauliMatrix[2] PauliMatrix[1] }
t = { trivectorType, I IdentityMatrix[2] }
m = { multivectorType, IdentityMatrix[2] + PauliMatrix[3] }
I can use these to specialize various operations. For example
directProduct[ t_, v1_, v2_ ] := { t, (v1 // Last). (v2 // Last) } ;
signedSymmetric[ t_, v1_, v2_, s_ ] := Module[ {a = (v1 // Last), b = (v2 // Last)}, {scalarType, (a . b + s b . a)/2} ] ;
symmetric[ t_, v1_, v2_ ] := signedSymmetric[ t, v1, v2, 1 ] ;
antisymmetric[ t_, v1_, v2_ ] := signedSymmetric[ t, v1, v2, -1 ] ;
ClearAll[ pScalarQ, pVectorQ, pBivectorQ, pTrivectorQ ]
pScalarQ[m : {scalarType, _}] := True ;
pScalarQ[m : {_Integer, _}] := False ;
pVectorQ[m : {vectorType, _}] := True ;
pVectorQ[m : {_Integer, _}] := False ;
...
Scalar[v_] := {scalarType, v IdentityMatrix[2]};
Vector[v_, k_Integer /; k > 0 && k < 4] := {vectorType, v PauliMatrix[k] };
Bivector[v_, k_Integer /; k > 0 && k < 4, j_Integer /; j > 0 && j < 4] := {bivectorType, v PauliMatrix[k] . PauliMatrix[j] };
Trivector[v_] := {trivectorType, v I IdentityMatrix[2]};
DotProduct[ v1_?pVectorQ, v2_?pVectorQ] := symmetric[ scalarType, v1, v2 ] ;
DotProduct[ v1_?pVectorQ, v2_?pBivectorQ] := antisymmetric[ vectorType, v1, v2 ] ;
...
WedgeProduct[ v1_?pVectorQ, v2_?pVectorQ ] := antisymmetric[ bivectorType, v1, v2 ] ;
WedgeProduct[ v1_?pVectorQ, v2_?pBivectorQ ] := symmetric[ trivectorType, v1, v2 ] ;
WedgeProduct[ v1_?pVectorQ, v2_?pTrivectorQ ] := Scalar[0] ;
GeometricProduct[ v1_, v2_ ] := directProduct[ multivectorType, v1, v2 ] ;
I like to do a few things:
0) Be able to distinguish my types from others in a robust way. For example, Sin[x] vs. my ad-hoc vector type {vectorType, ...}
1) Be able to multiply these objects by expressions, and extend Times in a natural way. For example, if that expression is not one of these types, then I would want the multiplication just apply to the second part, whereas Times for of object types such a vector, bivector would change the type to multivector, respect the multiplication order of the objects being multiplied, and multiply all the second parts of the objects respectively. Example:
Cos[x] * v
(v)(b)
> { vectorType, 3 Sin[x]Cos[x] PauliMatrix[2] }
> { multivectorType, 3 Sin[x] PauliMatrix[2] PauliMatrix[2] PauliMatrix[1] }
[EDIT: J.D suggests that I want to be customizing NonCommutativeMultiply instead of times, but doing so takes me back to problem (0). Also, I actually want object specific commutation selection: NonCommutativeMultiply when the objects have type vector or bivector or multivector, but Times when one of the factors is scalar or trivector].
2) I'd like to be able to implement a custom Plus function that behaves according to the underlying types. i.e. if the two objects match then the second part gets added, but if they are different types, the new result is a multivector (with the second parts also added). I have such an addition function, but it would be nicer if I did not have to call it explicitly.
3) Have the output of the result in the notebook look different than the internal representation. I think this amounts to overriding DisplayForm for each of my types, but I am not sure how to do that.
Finally, I suspect this is not a natural way to implement operators on a set of typed objects in Mathematica. If there is a better approach, I'd be interested in some tips for more effective strategies (an unfortunately open ended question).
Times[]
would be appropriate if multiplication of those objects was commutative (sinceTimes[]
has theOrderless
attribute); you might consider usingNonCommutativeMultiply[]
if multiplication isn't commutative. $\endgroup$UpValues
for those heads. A simple example is to denote scalars asa = scalar[3]
, and "overload" addition asscalar /: scalar[a_] + scalar[b_] := scalar[a+b]
. Then checking the "type" amounts to checking theHead
of stuff. $\endgroup$