0
$\begingroup$

Suppose I have some symbolic tensors $X,Y$ and I want to assign

X.Y

into something else without specifying what $Y$ is. For example, I may want $Y$ to serve as identity operator symbolically, i.e., $X.Y \to X$. Or perhaps I may want $X.Y \to YXY^\dagger$ in the sense of matrix multiplication. Is there a way to do this sort of re-assignment so that whenever I have $X.Y$ in the output, the Mathematica will simplify it according to what I assign it to?

I vaguely recall that Mathematica's way of assigning things is very powerful but I couldn't recall if there is a way to do this sort of thing. Note that ideally I would not want, for technical reasons, to do this by defining a function like

f[X_]:=Y.X.ConjugateTranspose[Y]

because I want to keep it symbolic, unless this is the only way to do it. Any suggestion is greatly appreciated.

Remark: this question is partly motivated by the need to avoid specifying what kind of objects involved, since what I intend to do is similar to here, but not having to know whether the objects are matrices. For example, if I have identity operator $I$ and Mathematica has no way of defining symbolic identity, then a natural way out would be to simply "re-assign" whatever I call $X.I$ and $I.X$ into $X$ manually and it is this "re-assignment by multiplication" that I would like to know if there is a way to do.

$\endgroup$
4
  • 1
    $\begingroup$ The desire to "keep it symbolic" is fine, and I respect that. But it will be a LOT easier if you don't conflate the appearance/form with the functionality. These different challenges can be worked on independently. Say you're pretty sure what the functionality should be but don't know the "symbolic" representation. Well, you can work on defining the function (e.g. MyFancyDot), getting that tested and working correctly, and defer dealing with the format/representation issues as a later, separate step. $\endgroup$
    – lericr
    Commented Jul 19, 2022 at 23:14
  • 1
    $\begingroup$ I would give more guidance if I understood more of you actually want this to work. You use "symbolic" several times, but I'm not sure what that covers. Are you literally wanting this framework to work with symbols with symbol names "X" and "Y" (and presumably others)? Or is it primarily the dot notation. Since these are tensors, does there need to be some translation to/from real tensor representations? In what sense do you need things to "work" (like are you expecting that certain things be automatically derived as a consequence of the types of X and Y? Etc. $\endgroup$
    – lericr
    Commented Jul 19, 2022 at 23:17
  • 1
    $\begingroup$ You can be bold about inventing new representations. There's nothing wrong with inventing a data type like BaseballTeam["Seattle","Mariners","2022"]. These can be static (they evaluate to themselves) or they can be constructors (they evaluate to some normalized form), but the important thing is that they can be operated on by functions to provide a lot of powerful functionality. Maybe a more relevant example would be CoolTensor[<some interesting characteristics of this tensor>]. Or even CoolTensor[Identity]. Or IdentityTensor[<the algebra in which it's an identity>]. $\endgroup$
    – lericr
    Commented Jul 19, 2022 at 23:26
  • $\begingroup$ @lericr makes sense, I think I haven't really needed to invent my own operations in this way, so it wasn't obvious to me this is the way to go. Thanks! $\endgroup$ Commented Jul 21, 2022 at 1:21

1 Answer 1

1
$\begingroup$

I will describe a few approaches. Every approach assumes that the kernel is fresh, with no definitions present. The reader is assumed to Quit[] before trying each approach, otherwise the approaches will interfere.

First approach (not recommended). If one interprets OP's question literally, one would redefine ., whose full name is Dot, and this can be done as follows:

Unprotect[Dot];
Dot[x_,Y] := Y.x.ConjugateTranspose[Y]; 
Protect[Dot];

There are a variety of problems with this. For example, if we enter

a.b.Y

what are we going to get? There are at least two possibilities:

  • The input could be interpreted as a.(b.Y) in which case the evaluation would give a.Y.b.ConjugateTranspose[Y] and then Y.a.ConjugateTranspose[Y].b.ConjugateTranspose[Y].

  • The input could be interpreted as (a.b).Y in which case we'd get Y.a.b.ConjugateTranspose[Y].

Now it turns out that we get the second of these, but it would have been difficult to guess. Perhaps we'd find an explanation somewhere in the documentation, but it is not the kind of thing one should rely on when writing robust code.

For a variety of reasons, it is usually not a good idea to modify built-in functions.

Second approach (not recommended). This is a variant of the first approach, where instead of unprotecting Dot one makes the same definition but as an upvalue for Y:

Y /: Dot[x_,Y] := Y.x.ConjugateTranspose[Y];

The main difference is that the definition is stored with the symbol Y rather than with the built-in symbol Dot. For more details see Evaluation of Expressions which is a must-read if one wants to set up ones own symbolic computation in Mathematica. See also Associating Definitions with Different Symbols and this.

Third approach. As @lericr points out, for more serious symbolic computation, one should set up ones own symbols. This can be tough because one has to make all definitions oneself. But at least there will be no interference with existing definitions, and there is no risk of messing up the built-in functions. So here one could define

myDot[x_,Y] := Y.x.ConjugateTranspose[Y];

On the right hand side I now used the ordinary . (which is a shorthand for Dot) but perhaps OP wants to use myDot on the right hand side, that depends on the application.

Btw, if one does not like the way the symbol is printed in the notebook, one can redefine it using Format, for example

Format[myDot[x__]]:=Infix[{x},"***"];

will make sure that an input such as myDot[a,b] is printed as a***b. I recommend not using Format when getting started with symbolic programming, since it can be confusing.

To be clear, in this way of doing things, nothing is automatic. For example, myDot[a,myDot[b,c]] and myDot[myDot[a,b],c] are not the same thing. If we want them to be the same thing, we could define

myDot[x___,myDot[y___],z___] := myDot[x,y,z];

which will transform both expressions above to myDot[x,y,z]. There are other ways to accomplish this kind of thing, including SetAttributes[myDot,{Flat,OneIdentity}].

Fourth approach. I do not understand what OP means with

Note that ideally I would not want, for technical reasons, to do this by defining a function like ... because I want to keep it symbolic ...

Functions are certainly very compatible with symbolic computation! In any case, let me mention that the things above can similarly be accomplished using rules, for example one could define

rls = {Dot[x_,Y] :> Y.x.ConjugateTranspose[Y]};

Then one could use something like

a.Y /. rls
(* shorthand for ReplaceAll[Dot[a,Y],rls] *)
$\endgroup$
1
  • 1
    $\begingroup$ This is extremely clear, thanks! I guess this is part of the bigger way of thinking about setting up one's own symbolic systems, such as if one is interested in making a non-commutative algebra. $\endgroup$ Commented Jul 21, 2022 at 1:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.