Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
mahrud opened this issue Aug 25, 2024 · 18 comments
Closed

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

mahrud opened this issue Aug 25, 2024 · 18 comments

Comments

@mahrud
Copy link
Member

mahrud commented Aug 25, 2024

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
Copy link
Member Author

mahrud commented Aug 25, 2024

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

@d-torrance
Copy link
Member

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

@d-torrance
Copy link
Member

d-torrance commented Aug 25, 2024

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
Copy link
Member

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
Copy link
Member Author

mahrud commented Aug 25, 2024

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
Copy link
Member

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
Copy link
Member Author

mahrud commented Aug 25, 2024

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
Copy link
Member

d-torrance commented Aug 25, 2024

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
Copy link
Member Author

mahrud commented Aug 25, 2024

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
Copy link
Contributor

pzinn commented Aug 26, 2024

the reason works (and more generally, a variety of mathematical symbols) is the following commits:
8936f64
ecde3f8
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
Copy link
Member Author

mahrud commented Aug 26, 2024

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.

@pzinn
Copy link
Contributor

pzinn commented Nov 18, 2024

why don't you use for \cdot?

i1 : ascii "⋅"

o1 = {226, 139, 133}

@pzinn
Copy link
Contributor

pzinn commented Nov 18, 2024

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})

@mahrud
Copy link
Member Author

mahrud commented Nov 18, 2024

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

@pzinn
Copy link
Contributor

pzinn commented Nov 18, 2024

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

@mahrud
Copy link
Member Author

mahrud commented Nov 18, 2024

What difference does it make?

@pzinn
Copy link
Contributor

pzinn commented Nov 18, 2024

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

@mahrud
Copy link
Member Author

mahrud commented Nov 18, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants