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

Polys: Refine dup_clear_denoms function and update tests #26180

Merged
merged 5 commits into from Feb 9, 2024

Conversation

mohakmalviya
Copy link
Contributor

@mohakmalviya mohakmalviya commented Feb 5, 2024

References to other Issues or PRs

Fixes: #26176

Brief description of what is fixed or changed

Refined the dup_clear_denoms function within the polys module, focusing on enhancing its efficiency and reliability when dealing with exact domains. The adjustments ensure that the function more accurately clears denominators, especially in scenarios involving symbolic expressions and rational numbers.

Other comments

Updated tests reflect these changes by focusing on:

  • Ensuring the convert flag functions as intended across different scenarios.

Release Notes

  • polys
    • Improved dup_clear_denoms function's efficiency in symbolic computations.

…module, focusing on enhancing its efficiency and reliability when dealing with exact domains. The adjustments ensure that the function more accurately clears denominators, especially in scenarios involving symbolic expressions and rational numbers.

Changes made include:
- Fine-tuning the logic for handling denominators to ensure robust performance across a variety of use cases.
- Streamlining the conversion process to guarantee that expressions are correctly converted to the target domain when requested.

The primary aim of these modifications was to address feedback and improve the function's handling of symbolic and rational expressions, thereby increasing the utility and reliability of polynomial operations within Sympy.

Updated tests reflect these changes by focusing on:
- Verifying the accurate clearing of denominators in exact domains.
- Ensuring the `convert` flag functions as intended across different scenarios.

While initial considerations included enhancements for inexact domain handling, this version concentrates on exact domains to solidify the foundation of `dup_clear_denoms`. Future work may revisit inexact domain improvements.

Fixes issue: sympy#26176 (sympy#26176)
@sympy-bot
Copy link

sympy-bot commented Feb 5, 2024

Hi, I am the SymPy bot. I'm here to help you write a release notes entry. Please read the guide on how to write release notes.

Your release notes are in good order.

Here is what the release notes will look like:

This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.13.

Click here to see the pull request description that was parsed.
<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->

Fixes: #26176

#### Brief description of what is fixed or changed

Refined the `dup_clear_denoms` function within the polys module, focusing on enhancing its efficiency and reliability when dealing with exact domains. The adjustments ensure that the function more accurately clears denominators, especially in scenarios involving symbolic expressions and rational numbers.

#### Other comments

Updated tests reflect these changes by focusing on:
- Ensuring the convert flag functions as intended across different scenarios.

#### Release Notes

<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:

* solvers
  * Added a new solver for logarithmic equations.

* functions
  * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

* physics.units
  * Corrected a semantical error in the conversion between volt and statvolt which
    reported the volt as being larger than the statvolt.

or if no release note(s) should be included use:

NO ENTRY

See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->

* polys
  * Improved `dup_clear_denoms` function's efficiency in symbolic computations.

<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

Copy link

github-actions bot commented Feb 5, 2024

Benchmark results from GitHub Actions

Lower numbers are good, higher numbers are bad. A ratio less than 1
means a speed up and greater than 1 means a slowdown. Green lines
beginning with + are slowdowns (the PR is slower then master or
master is slower than the previous release). Red lines beginning
with - are speedups.

Significantly changed benchmark results (PR vs master)

Significantly changed benchmark results (master vs previous release)

| Change   | Before [a00718ba]    | After [d5563740]    |   Ratio | Benchmark (Parameter)                                                |
|----------|----------------------|---------------------|---------|----------------------------------------------------------------------|
| -        | 68.3±0.6ms           | 44.0±0.3ms          |    0.64 | integrate.TimeIntegrationRisch02.time_doit(10)                       |
| -        | 68.6±1ms             | 43.2±0.2ms          |    0.63 | integrate.TimeIntegrationRisch02.time_doit_risch(10)                 |
| +        | 18.6±0.4μs           | 29.7±0.4μs          |    1.6  | integrate.TimeIntegrationRisch03.time_doit(1)                        |
| -        | 5.38±0.04ms          | 2.84±0.03ms         |    0.53 | logic.LogicSuite.time_load_file                                      |
| -        | 73.5±0.4ms           | 28.3±0.4ms          |    0.38 | polys.TimeGCD_GaussInt.time_op(1, 'dense')                           |
| -        | 25.8±0.04ms          | 16.7±0.06ms         |    0.65 | polys.TimeGCD_GaussInt.time_op(1, 'expr')                            |
| -        | 73.6±0.2ms           | 28.6±0.2ms          |    0.39 | polys.TimeGCD_GaussInt.time_op(1, 'sparse')                          |
| -        | 254±2ms              | 123±1ms             |    0.48 | polys.TimeGCD_GaussInt.time_op(2, 'dense')                           |
| -        | 254±1ms              | 123±1ms             |    0.48 | polys.TimeGCD_GaussInt.time_op(2, 'sparse')                          |
| -        | 647±3ms              | 366±0.9ms           |    0.57 | polys.TimeGCD_GaussInt.time_op(3, 'dense')                           |
| -        | 653±3ms              | 367±2ms             |    0.56 | polys.TimeGCD_GaussInt.time_op(3, 'sparse')                          |
| -        | 497±2μs              | 282±2μs             |    0.57 | polys.TimeGCD_LinearDenseQuadraticGCD.time_op(1, 'dense')            |
| -        | 1.81±0.01ms          | 1.05±0.01ms         |    0.58 | polys.TimeGCD_LinearDenseQuadraticGCD.time_op(2, 'dense')            |
| -        | 5.83±0.02ms          | 3.09±0.02ms         |    0.53 | polys.TimeGCD_LinearDenseQuadraticGCD.time_op(3, 'dense')            |
| -        | 449±2μs              | 227±2μs             |    0.51 | polys.TimeGCD_QuadraticNonMonicGCD.time_op(1, 'dense')               |
| -        | 1.48±0.01ms          | 669±3μs             |    0.45 | polys.TimeGCD_QuadraticNonMonicGCD.time_op(2, 'dense')               |
| -        | 4.85±0.02ms          | 1.66±0ms            |    0.34 | polys.TimeGCD_QuadraticNonMonicGCD.time_op(3, 'dense')               |
| -        | 381±2μs              | 205±0.7μs           |    0.54 | polys.TimeGCD_SparseGCDHighDegree.time_op(1, 'dense')                |
| -        | 2.45±0.03ms          | 1.24±0ms            |    0.5  | polys.TimeGCD_SparseGCDHighDegree.time_op(3, 'dense')                |
| -        | 10.0±0.05ms          | 4.33±0.04ms         |    0.43 | polys.TimeGCD_SparseGCDHighDegree.time_op(5, 'dense')                |
| -        | 362±1μs              | 171±1μs             |    0.47 | polys.TimeGCD_SparseNonMonicQuadratic.time_op(1, 'dense')            |
| -        | 2.48±0.01ms          | 884±10μs            |    0.36 | polys.TimeGCD_SparseNonMonicQuadratic.time_op(3, 'dense')            |
| -        | 9.59±0.05ms          | 2.68±0.01ms         |    0.28 | polys.TimeGCD_SparseNonMonicQuadratic.time_op(5, 'dense')            |
| -        | 1.04±0ms             | 427±6μs             |    0.41 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(3, 'dense')           |
| -        | 1.73±0.01ms          | 503±2μs             |    0.29 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(3, 'sparse')          |
| -        | 5.88±0.03ms          | 1.79±0.02ms         |    0.3  | polys.TimePREM_LinearDenseQuadraticGCD.time_op(5, 'dense')           |
| -        | 8.44±0.02ms          | 1.48±0.01ms         |    0.18 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(5, 'sparse')          |
| -        | 284±0.8μs            | 64.3±0.2μs          |    0.23 | polys.TimePREM_QuadraticNonMonicGCD.time_op(1, 'sparse')             |
| -        | 3.38±0.04ms          | 389±2μs             |    0.11 | polys.TimePREM_QuadraticNonMonicGCD.time_op(3, 'dense')              |
| -        | 4.00±0.04ms          | 279±1μs             |    0.07 | polys.TimePREM_QuadraticNonMonicGCD.time_op(3, 'sparse')             |
| -        | 7.15±0.07ms          | 1.24±0.01ms         |    0.17 | polys.TimePREM_QuadraticNonMonicGCD.time_op(5, 'dense')              |
| -        | 8.75±0.03ms          | 835±3μs             |    0.1  | polys.TimePREM_QuadraticNonMonicGCD.time_op(5, 'sparse')             |
| -        | 5.06±0.01ms          | 2.97±0.01ms         |    0.59 | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(2, 'sparse') |
| -        | 12.0±0.05ms          | 6.54±0.02ms         |    0.54 | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(3, 'dense')  |
| -        | 22.7±0.1ms           | 8.99±0.04ms         |    0.4  | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(3, 'sparse') |
| -        | 5.26±0.05ms          | 864±9μs             |    0.16 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(1, 'sparse')    |
| -        | 12.8±0.08ms          | 7.09±0.03ms         |    0.56 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(2, 'sparse')    |
| -        | 102±0.7ms            | 25.4±0.1ms          |    0.25 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(3, 'dense')     |
| -        | 169±0.3ms            | 53.3±0.2ms          |    0.31 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(3, 'sparse')    |
| -        | 175±0.5μs            | 112±0.9μs           |    0.64 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(1, 'dense')      |
| -        | 359±1μs              | 213±1μs             |    0.59 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(1, 'sparse')     |
| -        | 4.31±0.01ms          | 829±6μs             |    0.19 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(3, 'dense')      |
| -        | 5.25±0.02ms          | 381±0.8μs           |    0.07 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(3, 'sparse')     |
| -        | 19.9±0.1ms           | 2.81±0.02ms         |    0.14 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(5, 'dense')      |
| -        | 22.7±0.2ms           | 623±3μs             |    0.03 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(5, 'sparse')     |
| -        | 474±3μs              | 134±1μs             |    0.28 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(1, 'sparse') |
| -        | 4.75±0.02ms          | 619±5μs             |    0.13 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(3, 'dense')  |
| -        | 5.24±0.03ms          | 140±2μs             |    0.03 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(3, 'sparse') |
| -        | 13.3±0.1ms           | 1.29±0ms            |    0.1  | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(5, 'dense')  |
| -        | 14.0±0.1ms           | 139±0.5μs           |    0.01 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(5, 'sparse') |
| -        | 132±0.5μs            | 73.7±0.6μs          |    0.56 | solve.TimeMatrixOperations.time_rref(3, 0)                           |
| -        | 247±0.4μs            | 87.2±0.5μs          |    0.35 | solve.TimeMatrixOperations.time_rref(4, 0)                           |
| -        | 24.4±0.1ms           | 10.1±0.03ms         |    0.42 | solve.TimeSolveLinSys189x49.time_solve_lin_sys                       |
| -        | 28.5±0.3ms           | 15.5±0.2ms          |    0.54 | solve.TimeSparseSystem.time_linsolve_Aaug(20)                        |
| -        | 54.6±0.5ms           | 24.8±0.09ms         |    0.45 | solve.TimeSparseSystem.time_linsolve_Aaug(30)                        |
| -        | 28.1±0.2ms           | 15.4±0.1ms          |    0.55 | solve.TimeSparseSystem.time_linsolve_Ab(20)                          |
| -        | 54.2±0.3ms           | 24.6±0.1ms          |    0.45 | solve.TimeSparseSystem.time_linsolve_Ab(30)                          |

Full benchmark results can be found as artifacts in GitHub Actions
(click on checks at the top of the PR).

Comment on lines 624 to 626
assert dup_clear_denoms(
[QQ(3), QQ(1), QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3), QQ(1), QQ(0)])
assert dup_clear_denoms(
[QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2), QQ(1), QQ(0)])

assert dup_clear_denoms([QQ(3), QQ(
1), QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)])
assert dup_clear_denoms([QQ(1), QQ(
1, 2), QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)])
assert dup_clear_denoms([QQ(3), QQ(1), QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3), QQ(1), QQ(0)])
assert dup_clear_denoms([QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2), QQ(1), QQ(0)])

assert dup_clear_denoms(
[EX(S(3)/2), EX(S(9)/4)], EX) == (EX(4), [EX(6), EX(9)])
assert dup_clear_denoms([QQ(3), QQ(1), QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)])
assert dup_clear_denoms([QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)])
Copy link
Contributor

Choose a reason for hiding this comment

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

Why have these tests been changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sorry sir it seems like i haven't pushed the updated code because i have written the tests for RR.frac_field(x) but i can't see it in this code....sorry for this


assert dup_clear_denoms([EX(S(3)/2), EX(S(9)/4)], EX) == (EX(4), [EX(6), EX(9)])
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this example fail without the fix?

The problem in the issue was with the domain RR.frac_field(x).

Comment on lines 641 to 648
assert dup_clear_denoms([EX(7)], EX) == (EX(1), [EX(7)])
assert dup_clear_denoms([EX(sin(x)/x), EX(0)], EX) == (EX(x), [EX(sin(x)), EX(0)])

# This test is for inexact domain handling
R, x = ring("x", RR)
f = RR(0.5)*x + RR(0.333)
common, cleared_f = dup_clear_denoms(f, RR)
assert cleared_f == f, "Function should not modify input for inexact domains when no clear action is needed"
Copy link
Contributor

Choose a reason for hiding this comment

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

Look at the rest of the code in this file for examples of how to write the test code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Look at the rest of the code in this file for examples of how to write the test code.

sir i have used R.to_domain() with dup_decompose function but it does not seem to work

@oscarbenjamin
Copy link
Contributor

The operation to be tested is:

In [18]: F = RR.frac_field(x)

In [19]: dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F)
Out[19]: (x + 0.353356890459364, [(8.48717*x + 2.999)/(8.0089*x + 2.83), 0.0])

It is awkward to test output with floats but a string test is fine like `assert str(result) == "(x + ...".

@mohakmalviya
Copy link
Contributor Author

mohakmalviya commented Feb 6, 2024

The operation to be tested is:

In [18]: F = RR.frac_field(x)

In [19]: dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F)
Out[19]: (x + 0.353356890459364, [(8.48717*x + 2.999)/(8.0089*x + 2.83), 0.0])

It is awkward to test output with floats but a string test is fine like `assert str(result) == "(x + ...".

sir so do i have to add the test for specificaly this output? or it should be generalised for these type of polynomial expressions?

@mohakmalviya
Copy link
Contributor Author

mohakmalviya commented Feb 6, 2024

Out[19]: (x + 0.353356890459364, [(8.48717x + 2.999)/(8.0089x + 2.83), 0.0])

when i am running tests for the output - Out[19]: (x + 0.353356890459364, [(8.48717*x + 2.999)/(8.0089*x + 2.83), 0.0]) they are failing because dup_clear_denoms does not give output in fractional form, it should be converted first into simplified form
image

Comment on lines 646 to 649
result_common_denom, result_transformed_elements = dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F)
result_str = str((result_common_denom, result_transformed_elements))
expected_output_str = "(x + 0.353356890459364, [1.05971731448763, 0.0])"
assert result_str == expected_output_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 is unnecessarily complicated:

result = dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F)
assert str(result) == "(x + 0.353356890459364, [1.05971731448763, 0.0])"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sir i have updated it

@oscarbenjamin
Copy link
Contributor

Can you squash the commits?

@mohakmalviya
Copy link
Contributor Author

Can you squash the commits?

done sir

@oscarbenjamin
Copy link
Contributor

The commits are not squashed. There are 12 commits but there should only be one.

@mohakmalviya
Copy link
Contributor Author

mohakmalviya commented Feb 8, 2024

The commits are not squashed. There are 12 commits but there should only be one.

sir i am squashing the commits but it is still showing 12 on the commits tab
Edit : completed

…module, focusing on enhancing its efficiency and reliability when dealing with exact domains. The adjustments ensure that the function more accurately clears denominators, especially in scenarios involving symbolic expressions and rational numbers.

Changes made include:
- Fine-tuning the logic for handling denominators to ensure robust performance across a variety of use cases.
- Streamlining the conversion process to guarantee that expressions are correctly converted to the target domain when requested.

The primary aim of these modifications was to address feedback and improve the function's handling of symbolic and rational expressions, thereby increasing the utility and reliability of polynomial operations within Sympy.

Updated tests reflect these changes by focusing on:
- Verifying the accurate clearing of denominators in exact domains.
- Ensuring the `convert` flag functions as intended across different scenarios.

While initial considerations included enhancements for inexact domain handling, this version concentrates on exact domains to solidify the foundation of `dup_clear_denoms`. Future work may revisit inexact domain improvements.

Fixes issue: sympy#26176 (sympy#26176)

improved code quality

Updated PR with comments

Updated code quality

Updated test cases for domain handling

Updated code quality

Added test with simplified expression

Updated test

Improved code quality

Improved code quality

improved code quality

Updated PR with comments

Updated code quality

Updated test cases for domain handling

Updated code quality

Added test with simplified expression

Updated test

Improved code quality

Improved code quality

Squashed commits

Updated PR with comments

Updated code quality

Updated test cases for domain handling

Updated code quality

Added test with simplified expression

Updated test

Improved code quality

Improved code quality
@@ -637,6 +635,9 @@ def test_dup_clear_denoms():
assert dup_clear_denoms([EX(7)], EX) == (EX(1), [EX(7)])
assert dup_clear_denoms([EX(sin(x)/x), EX(0)], EX) == (EX(x), [EX(sin(x)), EX(0)])

F = RR.frac_field(x)
result = dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F)
assert str(result) == "(x + 0.353356890459364, [1.05971731448763, 0.0])"
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if it is generally a robust approach to use the str(result).
As far as I know, using exact value of float, can give fragile tests with
CPU vendors, math libraries, which may give slightly different numbers due to the implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

The floats used here are mpmath floats which are based on only integer arithmetic and should be the same on all hardware. There isn't any good function currently for comparing polyelement approximately over RR.

@sylee957 sylee957 merged commit 202c340 into sympy:master Feb 9, 2024
64 checks passed
@mohakmalviya mohakmalviya deleted the fix-coercion-integrals branch February 12, 2024 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CoercionFailed when integrating exponential functions. But only for some values - why?
5 participants