Skip to content

Adds limit(expr, v, a) syntax #39812

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 10 commits into from
May 11, 2025

Conversation

EigenVector22
Copy link
Contributor

Implemented the limit(expr, variable, value) positional syntax to allow limits with respect to indexed variables or other variables not usable as keyword arguments.

Also, Updated the documentation and added doctests for the new syntax and associated error handling. Ensuring code coverage for the new argument parsing.

Fixes #38761 by allowing limits to be taken with respect to indexed variables like x[0] or other symbolic expressions not usable as keyword arguments.

While testing, the tests passed except the optional fricas ones that were failing before too.

📝 Checklist

  • The title is concise and informative.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation and checked the documentation preview.

⌛ Dependencies

Got the idea and direction of fixing the issue and the link to the relevant conversations in the given PR: #38780

@EigenVector22
Copy link
Contributor Author

Hello, @vincentmacri, @nbruin,

can you please review this and
suggest if any thing else needs to be done,

Thanks,

@@ -429,13 +429,16 @@
from sage.misc.latex import latex
from sage.misc.parser import Parser, LookupNameMaker
from sage.structure.element import Expression
from sage.symbolic.expression import Expression
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this import? It shadows the one above. Figure out which one you need (why not the one that was already there?) and remove the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes you right, the second import just overwrites the 1st one, made the necessary changes
Thanks,

Copy link
Contributor

Choose a reason for hiding this comment

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

sage.structure.element.Expression and sage.symbolic.expression.Expression aren't the same. Do you have a reason to change which one is imported here? There was probably a good reason why the code here originally imported the abstract base class rather than the concrete one.

Copy link
Contributor

Choose a reason for hiding this comment

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

This still needs addressing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

okay, i guess i probably get this now, since the limit function is operating on symbolic expressions (converted via SR), so the abstract base class is probably sufficient, am i right?

@EigenVector22 EigenVector22 force-pushed the fix-limit-syntax-38761 branch from 28b5916 to f468ab1 Compare March 28, 2025 16:22
@vincentmacri
Copy link
Contributor

I'll leave commenting on the code itself to @nbruin since he wrote the first draft of this.

All I'll add is that I find the usage of "new syntax" versus "old syntax" in the docstrings a bit confusing. It makes it sounds like we plan to deprecate the old syntax, which I don't think we do plan on doing (or should plan on doing unless there's a good reason).

I'm not sure what terms would be better. Maybe just show examples of both without referring to one as being new or old, and just mention in the examples why the syntax you're adding here is preferred/needed in some situations. For example, when I added additional functionality to the subs method for function field elements I made sure to have examples of the different syntax you could use. Given the motivation of this PR being to be able to compute limits where the x=a syntax doesn't work, I would explicitly say in the example for the limit(expr, v, a) syntax when you might prefer to use it. (In retrospect I think the same issue with x=a that you're fixing here applied to what I did there for the subs method, I didn't mention it in the example for the dictionary syntax there because it didn't occur to me that you might have indexed variables or something, although the dictionary syntax should support it.)

@EigenVector22
Copy link
Contributor Author

Hello Vincent,

Thanks for your feedback on the documentation. I also think that labeling the syntaxes as "new" and "old" maybe could imply we are deprecating x=a, which isn’t the case—both syntaxes are meant to stay supported, as of now unless( as you said) ......

I just wanted to ask that what should be next course of action ?
Should I update the docstrings to like remove the "new" and "old" labels and instead present both syntaxes side-by-side with examples. I will TRY to clarify like when each one of them is appropriate to use

I am thinking to include something like this in the docstrings:
Use limit(expr, x=a) for straightforward variable substitutions, but opt for limit(expr, v, a) when v is an indexed variable like y[1], since keyword arguments can’t directly handle indexed names.

Is this the right way of representation?

Thanks,

@nbruin
Copy link
Contributor

nbruin commented Mar 29, 2025

I am thinking to include something like this in the docstrings: Use limit(expr, x=a) for straightforward variable substitutions, but opt for limit(expr, v, a) when v is an indexed variable like y[1], since keyword arguments can’t directly handle indexed names.

No, just be neutral:

There are two ways of invoking limit. One can write limit(expr, x=a, <keywords>) or limit(expr, x, a, <keywords>). In the first option, x must be a valid python identifier. Its string representation is used to create the corresponding symbolic variable with respect to which to take the limit. In the second option, x can simply be a symbolic variable. For symbolic variables that do not have a string representation that is a valid python identifier, for instance if x is an indexed symbolic variable, the second option is required.

@EigenVector22 EigenVector22 force-pushed the fix-limit-syntax-38761 branch 2 times, most recently from 05b3f07 to e806bf7 Compare March 29, 2025 09:30
@EigenVector22
Copy link
Contributor Author

I have made the necessary changes to the doctests and added examples where necessary, can you please review it.
Thanks,

@EigenVector22
Copy link
Contributor Author

@nbruin any other feedback regarding the code?

is_getitem = hasattr(v.operator(), '__name__') and v.operator().__name__ == '__getitem__'
if not (is_getitem and v.operands()):
# If it’s not an indexed variable, checking if it’s numeric
if v.is_numeric():
Copy link
Contributor

Choose a reason for hiding this comment

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

certainly not necessary to further differentiate if you know you're going to raise an error

if not (is_getitem and v.operands()):
# If it’s not an indexed variable, checking if it’s numeric
if v.is_numeric():
raise TypeError(f"Limit variable must be a variable, not a constant number: {v}")
Copy link
Contributor

Choose a reason for hiding this comment

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

Just raise a more generic message here. You don't need to give the value that is causing the error. That's in the backtrace.

try:
a = SR(a)
except TypeError:
raise TypeError(f"Cannot convert limit point to symbolic ring: {a}")
Copy link
Contributor

Choose a reason for hiding this comment

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

just let SR(a) raise the error.

@@ -1498,38 +1699,53 @@ def mma_free_limit(expression, v, a, dir=None):

EXAMPLES::
Copy link
Contributor

Choose a reason for hiding this comment

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

My expertise does not extend to mma_free_limit so I cannot review that code.

@EigenVector22 EigenVector22 force-pushed the fix-limit-syntax-38761 branch from e806bf7 to 40a04c1 Compare March 29, 2025 21:41
@EigenVector22
Copy link
Contributor Author

I've applied the suggested changes from the latest review.

  • Regarding the use of kwargv instead of argv: Initially, I intended to use kwargv since the documentation highlighted it as a
    standard practice. However, I later opted for argv, my bad, should my discussed this beforehand

  • I also removed some unnecessary, bloated lines of code from the PR. I've been working on this for 2–3 weeks, and the build
    broke a couple of times during that period. T_T

Everything has now been updated as suggested. Do you have any further feedback?

@EigenVector22 EigenVector22 requested a review from nbruin March 29, 2025 22:43
@EigenVector22
Copy link
Contributor Author

@vincentmacri any other feedback from your side ,?

Copy link
Contributor

@nbruin nbruin left a comment

Choose a reason for hiding this comment

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

Please make sure you address all concerns. I think I have reflagged issues that I flagged before and that did not receive a response.

If you disagree with my assessment on any one of the items you can respond with your reasons and then we can take it from there.

@@ -429,13 +429,16 @@
from sage.misc.latex import latex
from sage.misc.parser import Parser, LookupNameMaker
from sage.structure.element import Expression
from sage.symbolic.expression import Expression
Copy link
Contributor

Choose a reason for hiding this comment

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

This still needs addressing


if len(args) == 2: # Syntax: limit(ex, v, a, ...)
if kwargs: # Cannot mix positional v, a with keyword args
raise ValueError(f"Use either limit(expr, v, a, ...) or limit(expr, v=a, ...) syntax. "
Copy link
Contributor

Choose a reason for hiding this comment

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

Error message should be an uncapitalized phrase describing the problem; not advice. So:

'''
Cannot mix positional specification of limit variable and point with keyword variable arguments
'''

No need for arguments in message: those can be obtained from traceback.

I don't think advice of the form Use either ... or ... is necessary here. Any advice would go after the primary error message.

elif len(args) == 0: # Potential syntax: limit(ex, v=a, ...) or limit(ex)
if len(kwargs) == 1:
k, = kwargs.keys()
if not isinstance(k, str):
Copy link
Contributor

Choose a reason for hiding this comment

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

this test is not required:

sage: def f(*args,**kwargs): return args, kwargs
sage: f(**{10:1})
TypeError: keywords must be strings

k, = kwargs.keys()
if not isinstance(k, str):
raise ValueError(f"Invalid variable specification in keyword argument: {k} (must be a string)")
try:
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to guard with try. Just let error propagate.


# Ensuring v is a symbolic expression
if not isinstance(v, Expression):
try:
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to guard with "try". Just let it raise on error by itself

# Ensuring v is a symbolic expression
if not isinstance(v, Expression):
try:
v = SR(v)
Copy link
Contributor

Choose a reason for hiding this comment

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

create what you need: use

v=SR.symbol(v)

instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nbruin
This approach is creating an Error:

For the call

limit(x^2 + 5, 5, 10):

v = 5 is an integer, not an instance of Expression

SR.symbol(v) is called with v = 5, but SR.symbol() expects a string to create a symbolic variable. Passing integer like 5 causes a

TypeError: expected string or bytes-like object, got 'sage.rings.integer.Integer'.

I think this error is occuring because the code assumes v can be converted to a symbolic variable directly, but a constant like 5 isn’t a valid limit variable, limits must be taken with respect to a variable (e.g., x), not a constant,

its working fine with this approach:

# Ensuring v is a symbolic expression and a valid limit variable
if not isinstance(v, Expression):
    v = SR(v)
if not v.is_symbol():
    raise TypeError("limit variable must be a variable, not a constant")

v = var(k)
a = argv[k]
# Check if v is a valid limit variable
if not v.is_symbol() and v.is_constant():
Copy link
Contributor

Choose a reason for hiding this comment

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

you will have a symbol here with the change above, so no reason to check

@EigenVector22 EigenVector22 force-pushed the fix-limit-syntax-38761 branch from 40a04c1 to 8858361 Compare March 31, 2025 02:27
@EigenVector22
Copy link
Contributor Author

@nbruin does everything looks good now?

Copy link
Contributor

@nbruin nbruin left a comment

Choose a reason for hiding this comment

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

OK that looks fine now. I did find it painful in the review process to not have the comments and markings from the previous review available. Part of that might be github's interface. Another component is that you squash your commits in a forced push, destroying history. It might be better to just make successive commits. If you really want to squash before merge, you could squash at the very end to make a branch with a different (more concise) history but with the same effect on the tree.

@EigenVector22
Copy link
Contributor Author

@nbruin , thanks so much for reviewing the PR thoroughly and providing detailed feedback.
I have tried to incorporate your feedback into my recent PRs.

Regarding the comment on squashing commits with a forced push: I initially thought that working alone on the branch wouldn't be an issue, but I see how it complicates code review. I'll adjust my approach and make successive commits going ahead

Thanks again!

@EigenVector22
Copy link
Contributor Author

hello, @roed314 can you review and run the workflows if possible,
Thanks,

@roed314
Copy link
Contributor

roed314 commented Apr 1, 2025

Done. It still looks like there's something strange going on with force pushes (if you look at the Files Changed you'll see a bunch of changes that are just 10.6 vs 10.6.rc1).

Copy link

github-actions bot commented Apr 1, 2025

Documentation preview for this PR (built with commit ec6e145; changes) is ready! 🎉
This preview will update shortly after each push to this PR.

Comment on lines 1218 to 1220
The positional `limit(expr, v, a)` syntax is particularly useful when
the limit variable `v` is an indexed variable or another expression
that cannot be used as a keyword argument (fixes :issue:`38761`)::
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The positional `limit(expr, v, a)` syntax is particularly useful when
the limit variable `v` is an indexed variable or another expression
that cannot be used as a keyword argument (fixes :issue:`38761`)::
The positional ``limit(expr, v, a)`` syntax is particularly useful
when the limit variable ``v`` is an indexed variable or another
expression that cannot be used as a keyword argument
(fixes :issue:`38761`)::

When referring to code, typeset it with two ticks (code) rather than one tick (LaTeX).

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

@vincentmacri vincentmacri left a comment

Choose a reason for hiding this comment

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

Minor formatting fix

"""
from sage.interfaces.mathematica import request_wolfram_alpha, parse_moutput_from_json, symbolic_expression_from_mathematica_string
Copy link
Contributor

Choose a reason for hiding this comment

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

did you test this code? I would expect you can't just remove an import and expect the code will still run. I can't find another place where these functions are imported. Do you actually have a reason to touch the mma_free_limit code in this ticket? It doesn't seem to have anything to do with the topic of the ticket.

The doc build error is separate from this issue, as far as I can tell.

Copy link
Contributor

Choose a reason for hiding this comment

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

I do have the same question as you as to why this code was removed (I'm guessing an accident). But I tested it and putting this back doesn't seem to fix the issue with giac.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

regarding the mma_free_limit i tried to kind of optimize it, now it seems it was not needed/ also have some issues in implementing, will again look into it and if required will revert to the original implementation,
also,

I can't find another place where these functions are imported

i saw them here: src/sage/symbolic/integration/external.py,

Copy link
Contributor

Choose a reason for hiding this comment

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

it goes a little further than "some issues". You made the code non-functional in several places that are immediately obvious when you test the code. By removing the import you made sure that the code would eventually run into an undefined identifier, and by changing _mathematica_init_ to _mathematica_ you ensured that on systems where mathematica isn't available you wouldn't even get to that point. Such a level of carelessness makes me worried about any code you're submitting.

If you want to "optimize" some code you should at the very least check that the optimized code is still functional!

Copy link
Member

Choose a reason for hiding this comment

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

one can/should get a free copy of console Mathematica, a.k.a. "WolframScript" for testing these things. a soft link from wolframscript to math will need to be made in a place in your PATH.

Copy link
Contributor

Choose a reason for hiding this comment

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

this routine queries wolfram alpha. That's why it's labelled "optional - internet". It's undoubtedly possible to interface with yet another wolfram authored tool, but that's not what this routine is for at the moment. There is no need for additional software to test this routine. Just a network connection.

@vincentmacri
Copy link
Contributor

I have no idea why giac broke here, nothing you did should be causing this issue as far as I can tell. My best guess is some kind of circular import nonsense. I think I have a possible workaround though.

Right now the code calls giac by (attempting to) import libgiac from sage.libs.giac.giac. Maybe there's a way to use from sage.interfaces.giac import giac instead, as we are doing for fricas? I'm not too sure how this interface works though.

a = a._mathematica_init_()

try:
math_expr = expression._mathematica_().name()
Copy link
Contributor

Choose a reason for hiding this comment

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

this whole routine needs to be reverted.

On a system that does not have mathematica installed, _mathematica_() raises an error, whereas _mathematica_init_() doesn't. This routine should be making a web request. It should not be trying to run mathematica locally.

I'm pretty sure that all edits made to this routine are erroneous. They are certainly outside the stated scope of the pull request. If there is a change to be made to mma_free_limit it needs to happen on a different pull request.

@EigenVector22
Copy link
Contributor Author

(the error is actually on the line numbered 4016. I don't know why the link below seems to refer to line 4022. If I follow the link, I'm ending up at the correct OSError).

thanks for spotting the error, looking into it now.

@vincentmacri
Copy link
Contributor

vincentmacri commented May 2, 2025

(the error is actually on the line numbered 4016. I don't know why the link below seems to refer to line 4022. If I follow the link, I'm ending up at the correct OSError).

thanks for spotting the error, looking into it now.

The Inline substitution_reference start-string without end-string error is because you're missing a new line before the code block.

@vincentmacri
Copy link
Contributor

(the error is actually on the line numbered 4016. I don't know why the link below seems to refer to line 4022. If I follow the link, I'm ending up at the correct OSError).

thanks for spotting the error, looking into it now.

The Inline substitution_reference start-string without end-string error is because you're missing a new line before the code block.

But I agree with @nbruin, leave the changes to mma_free_limit to another PR. So fixing that error is a moot point for this PR.

@vincentmacri
Copy link
Contributor

I think the error with giac occurs on develop, you've just added a test that exposes the error. I think it might be related to recent changes by @orlitzky to how Sage interfaces with giac.

@orlitzky do you have any ideas? Does this code need to change to use the giac interface instead of the giac lib?

@orlitzky
Copy link
Contributor

orlitzky commented May 2, 2025

I think instead of # optional - giac, you want # needs sage.libs.giac. Otherwise the tests may still run when giac is installed but the sage interface to it has not been built.

@EigenVector22 EigenVector22 force-pushed the fix-limit-syntax-38761 branch from b42232e to dba0423 Compare May 3, 2025 10:31
@EigenVector22
Copy link
Contributor Author

EigenVector22 commented May 3, 2025

doing this only eliminates the giac failure, but the ones related to fricas still exists, my bad, I by mistake force pushed T_T

Copy link
Contributor

@vincentmacri vincentmacri left a comment

Choose a reason for hiding this comment

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

Looks good, one minor nitpick that you can take or leave.

@EigenVector22
Copy link
Contributor Author

@vincentmacri this is so strange, before incorporating your suggestion all the CIs successfully passed, now one of the CIs is failing, and i don't think that this is anything related to your suggestion that i committed? could there be any problem with the CIs ? do we need to run them again?

@vincentmacri
Copy link
Contributor

@vincentmacri this is so strange, before incorporating your suggestion all the CIs successfully passed, now one of the CIs is failing, and i don't think that this is anything related to your suggestion that i committed? could there be any problem with the CIs ? do we need to run them again?

I think it is a problem with the CI. You can try running that test locally with sage -t --long --warn-long 30.0 --random-seed=286735480429121101562228604801325644303 src/sage/doctest/test.py. If it fails check whether or not it also fails on develop.

@EigenVector22 EigenVector22 requested a review from dimpase May 4, 2025 19:10
@nbruin
Copy link
Contributor

nbruin commented May 5, 2025

The CI hiccup seems to have resolved itself. I'm OK with the present state:
ec6e145
if @vincentmacri or @dimpase agree, please put the state to "positive" so that the PR can be merged. Congratulations on getting this done! I'm sure it's been an instructive experience :-).

@EigenVector22
Copy link
Contributor Author

Thank you, @nbruin for all the intense code reviews and feedbacks, and thanks to @vincentmacri and @dimpase for all the guidance and your time. I've learned a lot through this PR.

vbraun pushed a commit to vbraun/sage that referenced this pull request May 6, 2025
sagemathgh-39812: Adds limit(expr, v, a) syntax
    
Implemented the limit(expr, variable, value) positional syntax to allow
limits with respect to indexed variables or other variables not usable
as keyword arguments.

Also, Updated the documentation and added doctests for the new syntax
and associated error handling. Ensuring code coverage for the new
argument parsing.

Fixes sagemath#38761  by allowing limits to be taken with respect to indexed
variables like x[0] or other symbolic expressions not usable as keyword
arguments.

While testing, the tests passed except the optional fricas ones that
were failing before too.

<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->



### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

Got the idea and direction of fixing the issue and the link to the
relevant conversations in the given PR: sagemath#38780
<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#39812
Reported by: Ashutosh Rajora
Reviewer(s): Ashutosh Rajora, Dima Pasechnik, nbruin, Vincent Macri
@vbraun vbraun merged commit 4dfd788 into sagemath:develop May 11, 2025
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unable to compute limit() over a variable coming from a list
7 participants