Skip to content

And mathML(Complex) and related updates #3726

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

Merged
merged 8 commits into from
May 15, 2025

Conversation

d-torrance
Copy link
Member

@d-torrance d-torrance commented Apr 9, 2025

We introduce a new type, ComplexExpression, to unify the printing of both the legacy ChainComplex and the newer Complex types. In particular, the latter didn't have a mathML method, and so they previously weren't supported in TeXmacs or Jupyter when using TeXmacs mode:

image

Now, net, texMath, and mathML just call expression first, and so we only need to implement each once and they'll work for both types:

image

A couple other related changes:

  • We add short(Module) for use in complex expressions. It uses moduleAbbrv (which has been slightly rewritten to just return null instead of having a backup representation) to use the module's variable name when possible, unless the module is free over a ring that's been assigned to a variable, in which case we use that respresentation.
  • We try to use substitute when building a legacy complex using chainComplex if the differential maps are given over different rings. This is to fix some issues with value(ComplexExpression), where the differential maps are MatrixExpression objects that may have forgotten some important info like what rings they were over. value(ComplexExpression) isn't perfect -- it still fails in other situtations like when we've lost degree information.

@d-torrance d-torrance requested a review from pzinn April 9, 2025 12:10
@pzinn
Copy link
Contributor

pzinn commented Apr 9, 2025

Looking good. I tried your code on M2web and I get:
Screenshot from 2025-04-09 15-11-10
I wonder how it will look after your PR?

@d-torrance
Copy link
Member Author

It should look the same. texMath(ComplexExpression) is essentially identical to the old texMath(ChainComplex). There were some subtle differences between that and texMath(Complex) in how to "shorten" the differential maps. The ChainComplex version used short(Matrix) and the Complex version rolled its own texMatrixShort, but they were pretty similar, so I just went with short(Matrix) for everything.

@pzinn
Copy link
Contributor

pzinn commented Apr 9, 2025

great. yeah texMatrixShort was supposed to be gone but it somehow resurfaced in Complex...

@d-torrance
Copy link
Member Author

Yikes! Apparently constructing these expressions isn't very efficient:

i9 : elapsedTime T = tateExtension W;
 -- .420421s elapsed

i10 : elapsedTime expression T;
 -- 12.5284s elapsed

I'll see if I can improve things.

@pzinn
Copy link
Contributor

pzinn commented Apr 9, 2025

one potential issue (I'm not sure if that's the one you're facing in your example) is that you probably don't want to take the expression (MatrixExpression) of a huge matrix since it literally spits out the list of entries (even if eventually it shortens it as in the current display in WebApp mode).

@d-torrance
Copy link
Member Author

Lol yeah -- that's exactly what's happening:

i12 : profileSummary

o12 = #run     %time   position                                                       
      1        93.71   src/macaulay2/M2/M2/Macaulay2/m2/chaincomplexes.m2:135:4-137:51
      16       93.7    src/macaulay2/M2/M2/Macaulay2/m2/matrix.m2:217:4-217:42        
      4586444  44.17   src/macaulay2/M2/M2/Macaulay2/m2/polyrings.m2:205:61-205:73    

4.5 million calls to expression 0...

@pzinn
Copy link
Contributor

pzinn commented Apr 9, 2025

on vanilla, I resolved this by having short act at the level of Matrix directly -- but I think you've reached the same conclusion. the only problem being, value won't work any more -- but I think that's an acceptable price to pay.

@mahrud
Copy link
Member

mahrud commented Apr 9, 2025

What's the point of an intermediary type ComplexExpression? I want to remove SheafExpression already.

@d-torrance
Copy link
Member Author

What's the point of an intermediary type ComplexExpression? I want to remove SheafExpression already.

For the time being, while we have two distinct complex types with Complex and ChainComplex, it gives us a way to write the various printing methods just once.

Also, based on the Expression docs, it seems like having expression classes for mathematical objects is part of the design of the language:

they are an intermediate phase in the conversion of a mathematical object to a net that can be printed

What's the problem with SheafExpression?

@pzinn
Copy link
Contributor

pzinn commented Apr 11, 2025

I think the only problem with your short(Matrix) is that it doesn't correctly treat zero matrices? (comparing quickly with the code on vanilla which I never got around to PRing)

@d-torrance
Copy link
Member Author

Oh ok -- I didn't think about zero matrices. I just used your commit instead.

@d-torrance
Copy link
Member Author

Hmm, there's something wrong with toString(MatrixExpression) after cherry-picking your commits, even live on the webapp:

i2 : toString short map(ZZ^2, ZZ^2, 0)

stdio:2:8:(3)]: error: expected a list, sequence, string, net, hash table, database, or dictionary

@pzinn
Copy link
Contributor

pzinn commented Apr 11, 2025

interesting. this is a good opportunity to clean up/debug this code...

@d-torrance d-torrance added waiting for review Core Issues involving the Core scripts. labels Apr 12, 2025
else if hasAttribute(M, ReverseDictionary)
then getAttribute(M, ReverseDictionary))

short Module := M -> moduleAbbrv M ?? expression M
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you removed the second input of moduleAbbrv, when this could have been implemented as:

Suggested change
short Module := M -> moduleAbbrv M ?? expression M
short Module := M -> moduleAbbrv(M, expression M)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've always thought that moduleAbbrv's two inputs were kind of clunky. For example, in Matrix.AfterPrint, we don't need the second input at all -- if we can't abbreviate the module, then we just don't print anything. I figured this was an opportunity to update it.

@d-torrance
Copy link
Member Author

I've updated the behavior of MatrixExpression for zero matrices that are either 0x0 or produced by short. The MatrixExpression object now has a zero option, whose value is a two-element sequence containing the target and source. This way, we have enough information to regenerate the matrix when using value.

@d-torrance
Copy link
Member Author

@sashahbc: Sorry to keep bugging you with these ToricHigherDirectImages issues!

Test 7 is failing on the builds for this PR:

i9 : assert(presentation RD2 == matrix {{y_1,0},{0,y_1^2},{0,0}})
stdio:10:6:(3): error: assertion failed

The rows were permuted a bit from the expected matrix:

i55 : presentation RD2

o55 = | 0   0     |
      | y_1 0     |
      | 0   y_1^2 |

              3      2
o55 : Matrix Q  <-- Q

From what I can tell, RD2 is the first homology of a complex generated by the unexported HDIComplex method. And its main loop is:

    directSum for mu in keys eigenchars list (

Since eigenchars is a hash table, we're not sure what order its keys will appears in the list returned by keys. Could that be the issue?

@sashahbc
Copy link
Contributor

Yeah that's definitely the problem. Not sure what the best solution would be. I was hoping components would detect the different summands but once we've taken homology I guess it loses track of that.

@sashahbc
Copy link
Contributor

It seems like a bad idea because it's a probabilistic test but could we do
assert( isIsomorphic( RD2, coker matrix {{y_1,0},{0,y_1^2},{0,0}} )_0 )?

@d-torrance
Copy link
Member Author

I suppose that would work. Is it okay that the order of the rows isn't deterministic, though?

@sashahbc
Copy link
Contributor

Yeah I am a bit uncomfortable with the order changing. Mahrud had a suggestion which I'll make a PR for in a sec.

@d-torrance d-torrance force-pushed the complex-expression branch 3 times, most recently from ec0f2ee to e41348b Compare April 18, 2025 13:33
@d-torrance d-torrance requested a review from mikestillman April 18, 2025 15:42
Blocks => null,
Degrees => null,
MutableMatrix => false,
zero => null};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason zero is the only lowercase option? and incidentally which type is this zero?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with zero since it's already exported. When it's non-null, it will be a sequence of two modules (the target and source).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant, what is the class of zero itself, not the option labelled zero.
usually it's nice if all options have the same type; which one is a matter of taste, it seems (all symbols seems the most common choice, though there's serious overhead in doing that; I would think all strings would actually make more sense, but it's rarely used. I digress)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an excellent question! I thought I'd put symbol zero here, but I didn't. And zero is a function closure, which should have raised an error...

i1 : new OptionTable from {zero => null}
stdio:1:0:(3): error: expected option key to be a symbol, a string, or a type

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhh I figured it out -- zero is still a symbol at this point since it's not defined as a function until integers.m2, which is later in the load sequence. (Same with MutableMatrix -- it's a type defined in mutablemat.m2, but it's a symbol here).

I'll change this so they're both explicitly symbols, just in case any code gets moved around

@@ -215,6 +215,7 @@ protect Blocks
blockMatrixForm=false; -- governs expression Matrix inclusion of blocks
expression Matrix := m -> (
x := applyTable(entries m, expression);
if #x == 0 then x = {symbol zero => (target m, source m)};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this line is not above the previous one (why go through all the entries if the matrix is zero anyway?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err, I mean, and why the test isn't simply x==0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've updated this so expression of a zero matrix always just uses the short form and never deals with the individual entries.

@d-torrance d-torrance force-pushed the complex-expression branch 2 times, most recently from 96ee033 to 95a577e Compare May 3, 2025 21:57
Copy link
Member

@mahrud mahrud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR conflicts heavily with the chain complex changes.

@d-torrance
Copy link
Member Author

Yeah, with both complex types moving to their own packages, I don't know if having a shared expression type in Core makes sense any more...

I think I'll trim this down to just the MatrixExpression changes and adding mathML(Complex).

@d-torrance d-torrance force-pushed the complex-expression branch from 95a577e to d46e46e Compare May 10, 2025 02:50
@d-torrance d-torrance changed the title Add ComplexExpression for expression-ifying ChainComplex and Complex objects And mathML(Complex) and related updates May 10, 2025
@@ -137,15 +137,15 @@ net ChainComplex := C -> (
a := s#0;
b := s#-1;
horizontalJoin between(" <-- ", apply(a .. b,i ->
stack (net moduleAbbrv(C_i, C_i)," ",net i)))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is moved in the Complexes PR, so you might as well rebase on top of that PR I think.

@d-torrance d-torrance force-pushed the complex-expression branch from d46e46e to de3a185 Compare May 10, 2025 03:58
d-torrance and others added 8 commits May 12, 2025 21:33
For nonfree modules, use the variable name if one exists.

This will replace moduleAbbrv when printing complexes.  We rewrite
moduleAbbrv to return null when we can't abbreviate it and leave it up
to the caller (now only AfterPrint for Matrix/Vector) to handle what
to do with it.
@d-torrance d-torrance force-pushed the complex-expression branch from de3a185 to 025a54c Compare May 13, 2025 01:35
@mikestillman
Copy link
Member

This is probably not relevant to what you are doing here, but it is something that is a problem.

 M = random(ZZ^2000, ZZ^2000);
13 M

or even

try(13 M; false) else true

This last one takes about 30 seconds on my laptop...

Copy link
Member

@mikestillman mikestillman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Thanks! It appears that much of the discussion in the PR is related to pre-chain complex movement to OldChainComplexes, Complexes. Sorry about that!

@pzinn Is there a regression in the web printing of Complexes? Are we using something that you got rid of, without our noticing?

@d-torrance d-torrance merged commit ac7b8b9 into Macaulay2:development May 15, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Core Issues involving the Core scripts. waiting for review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants