M2 icon indicating copy to clipboard operation
M2 copied to clipboard

Feature request: cdot or ⋅ as a binary operator

Open mahrud opened this issue 1 year ago • 11 comments

Currently we have a number of unicode synonyms defined in exports.m2. I think it would be useful to introduce the interpuct ($\cdot$) as a binary operator. This could be implemented as a synonym to a binary keyword cdot (similar to and, or, etc. but bound to a top-level method).

However, it would even be more interesting if M2-mode (and other editors) could automatically convert \cdot to $\cdot$ similar to, for instance, in agda-mode, and we could freely type C⋅D (without spaces) and have M2 recognize this.

This would be pretty straightforward, mostly duplicating sections related to and in the interpreter, but there are two tricky things:

  1. What's the best way to allow installing methods from top-level?
  2. How do we parse as a keyword operator in the interpreter so that C⋅D works?

The main applications for this is intersection product and of course dot product of vectors. If this goes well, I think replacing @@ with circ and $\circ$ would be amazing!

mahrud avatar Aug 25 '24 17:08 mahrud

This is of course related to #1069 for working with unicode characters. @pzinn have you tried something like this?

mahrud avatar Aug 25 '24 17:08 mahrud

What would the precedence be? Maybe between +/-/++ and **?

d-torrance avatar Aug 25 '24 22:08 d-torrance

M-x set-input-method followed by TeX already gives us the ability in Emacs to enter · using \cdot!

Edit: This isn't a good solution because it also changes the behavior of _ and ^ so that they trigger inputting little subscript and superscript characters.

d-torrance avatar Aug 25 '24 22:08 d-torrance

I played around with this and it's pretty straightforward to add:

diff --git a/M2/Macaulay2/d/actors5.d b/M2/Macaulay2/d/actors5.d
index fdde6bcd9a..821f671d57 100644
--- a/M2/Macaulay2/d/actors5.d
+++ b/M2/Macaulay2/d/actors5.d
@@ -198,6 +198,9 @@ setup(AmpersandS,ampersandfun);
 hathatfun(lhs:Code,rhs:Code):Expr := binarymethod(lhs,rhs,HatHatS);
 setup(HatHatS,hathatfun);
 
+interpunctfun(lhs:Code, rhs:Code):Expr := binarymethod(lhs, rhs, InterpunctS);
+setup(InterpunctS, interpunctfun);
+
 Tildefun(rhs:Code):Expr := unarymethod(rhs,TildeS);
 setuppostfix(TildeS,Tildefun);
 
diff --git a/M2/Macaulay2/d/binding.d b/M2/Macaulay2/d/binding.d
index a561f8249d..9640a02138 100644
--- a/M2/Macaulay2/d/binding.d
+++ b/M2/Macaulay2/d/binding.d
@@ -285,6 +285,8 @@ bumpPrecedence();
      export MinusS := makeKeyword(unarybinaryleft("-"));           -- also binary
      export PlusS := makeKeyword(unarybinaryleft("+"));            -- also binary
      export PlusPlusS := makeKeyword(binaryleft("++"));
+bumpPrecedence();
+     export InterpunctS := makeKeyword(binaryleft("·"));
 bumpPrecedence();
      export StarStarS := makeKeyword(binaryleft("**"));
 bumpPrecedence();
@@ -510,7 +512,8 @@ export opsWithBinaryMethod := array(SymbolClosure)(
      PowerGreaterEqualS,   UnderscoreGreaterEqualS,
      PowerLessS,           UnderscoreLessS,
      PowerLessEqualS,      UnderscoreLessEqualS,
-     PowerStarStarS
+     PowerStarStarS,
+     InterpunctS
      );
 export opsWithUnaryMethod := array(SymbolClosure)(
      StarS, MinusS, PlusS, LessLessS, QuestionQuestionS,
diff --git a/M2/Macaulay2/m2/exports.m2 b/M2/Macaulay2/m2/exports.m2
index c52ac919ae..c28facd05c 100644
--- a/M2/Macaulay2/m2/exports.m2
+++ b/M2/Macaulay2/m2/exports.m2
@@ -57,6 +57,7 @@ export {
        "_>=",
        "_<",
        "_<=",
+       "·",
        "Acknowledgement",
        "AdditionalPaths",
        "Adjacent",

It seems to then work out of the box:

i1 : Vector · Vector := (v, w) -> ((transpose v#0) * w#0)_(0, 0)

o1 = FunctionClosure[stdio:1:20-1:60]

o1 : FunctionClosure

i2 : vector {1,2,3} · vector {4, 5, 6}

o2 = 32

d-torrance avatar Aug 25 '24 22:08 d-torrance

Fantastic! Is u·v (without spaces) parsed correctly? How about things like locate? (e.g. if you look at the pseudocode, is the location of the operator correct and everything after it correct, or is it off because of unicode?)

I think we should still add cdot (e.g. to be used from the terminal). I imagine a couple of other places also need to updated (e.g. for converting to latex or for expressions).

M-x set-input-method followed by TeX already gives us the ability in Emacs to enter · using \cdot!

This is a great tip! C-\ TeX also seems to work and after the first time C-\ alone will toggle it on and off.

However, it opens a can of worms: should we consider supporting tex input by default? e.g. should $x^2$ work? How about printing this way depending on compactMatrixForm?

mahrud avatar Aug 25 '24 22:08 mahrud

Parsing without spaces doesn't work:

i4 : v·w

o4 = v·w

o4 : Symbol

But locate seems to work:

i6 : methods symbol ·

o6 = {0 => ((·, =), Type, Type)  }
     {1 => ((·, =), Thing, Thing)}
     {2 => (·, Thing, Thing)     }
     {3 => (·, Vector, Vector)   }

o6 : NumberedVerticalList

i7 : locate 3

o7 = stdio:1:20-1:60

o7 : FilePosition

i8 : code oo

o8 = stdio:1:20-1:60: --source code:
     Vector · Vector := (v, w) -> ((transpose v#0) * w#0)_(0, 0)

d-torrance avatar Aug 25 '24 23:08 d-torrance

That's not what I meant, is this output the same if you replace + with cdot?

i2 : peek locate (pseudocode functionBody(() ->   1     +     1))

o2 = FilePosition{stdio, 2, 45, 2, 58, 2, 51}

mahrud avatar Aug 25 '24 23:08 mahrud

It ends at column 59 instead of 58, so I'm guessing that's coming from the extra byte in the Unicode character.

i2 : peek locate (pseudocode functionBody(() ->   1     ·     1))

o2 = FilePosition{stdio, 2, 45, 2, 59, 2, 51}

d-torrance avatar Aug 25 '24 23:08 d-torrance

Parsing without spaces doesn't work:

It would be ideal if we could get this to work, for instance A⊗B seems to parse correctly:

i25 : A⊗B
stdio:25:0:(3): error: no method for binary operator ** applied to objects:
            A (of class Symbol)
     **     B (of class Symbol)

mahrud avatar Aug 25 '24 23:08 mahrud

the reason works (and more generally, a variety of mathematical symbols) is the following commits: 8936f64c85b7eddc83aef6e6c8fa3edff3cf28db ecde3f8a77d02cef36b0e58352aa5089a8cf76db Sadly this cdot is not a "mathematical" symbol -- its unicode doesn't start with 226 -- so the code there doesn't immediately apply. one could try to rewrite these commits to allow for general unicode symbols, but that will require some significant changes to the parser.

pzinn avatar Aug 26 '24 00:08 pzinn

Thanks! This is very helpful. Taking a brief look, is it not possible to add cdot as an exception? I don't think there are too many of them.

mahrud avatar Aug 26 '24 01:08 mahrud

why don't you use for \cdot?

i1 : ascii "⋅"

o1 = {226, 139, 133}

pzinn avatar Nov 18 '24 00:11 pzinn

Incidentally, if you want to see all characters starting with 226 (which contains a bunch of non mathematical symbols as well -- we could be more selective), try in the browser:

needsPackage "Text"; TABLE table(2^6,2^6,(i,j)->ascii{226,128+i,128+j})

pzinn avatar Nov 18 '24 00:11 pzinn

v·w still won't work. This requires a change in the interpreter.

mahrud avatar Nov 18 '24 00:11 mahrud

actually in your original post you used ·, not ·. why would you use the latter?

pzinn avatar Nov 18 '24 00:11 pzinn

What difference does it make?

mahrud avatar Nov 18 '24 00:11 mahrud

starts with 226 in UTF8, so there's no need to change existing code besides what @d-torrance did.

pzinn avatar Nov 18 '24 00:11 pzinn

In emacs, open M2 then enter M-x set-input-method and type TeX. Then type ascii "\cdot". It'll be converted to this:

i1 : ascii "·"

o1 = {194, 183}

We want something that is easily usable.

mahrud avatar Nov 18 '24 12:11 mahrud