Macaulay2 / M2

The primary source code repository for Macaulay2, a system for computing in commutative algebra, algebraic geometry and related fields.
https://macaulay2.com
343 stars 229 forks source link

Feature request: cdot or ⋅ as a binary operator #3434

Open mahrud opened 2 weeks ago

mahrud commented 2 weeks ago

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 commented 2 weeks ago

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

d-torrance commented 2 weeks ago

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

d-torrance commented 2 weeks ago

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 commented 2 weeks ago

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
mahrud commented 2 weeks ago

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?

d-torrance commented 2 weeks ago

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)
mahrud commented 2 weeks ago

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}
d-torrance commented 2 weeks ago

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}
mahrud commented 2 weeks ago

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)
pzinn commented 2 weeks ago

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.

mahrud commented 2 weeks ago

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.