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

[WIP] Adding ParametricIntegral class #19539

Merged
merged 8 commits into from
Jun 25, 2020
Merged

[WIP] Adding ParametricIntegral class #19539

merged 8 commits into from
Jun 25, 2020

Conversation

friyaz
Copy link
Member

@friyaz friyaz commented Jun 11, 2020

References to other Issues or PRs

#19320

Brief description of what is fixed or changed

An object of ParametricIntegral represents integral of a scalar or vector field over a Parametric Region.

Other comments

_bounds_case function is taken from @prasoon2211 PR with some modification. I have added him as a coauthor for the corresponding commit.

Release Notes

  • vector
    • added class to represent integral of scalar/vector field over a parametric surface.

@sympy-bot
Copy link

sympy-bot commented Jun 11, 2020

Hi, I am the SymPy bot (v160). 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:

  • vector
    • added class to represent integral of scalar/vector field over a parametric surface. (#19539 by @friyaz)

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

Note: This comment will be updated with the latest check if you edit the pull request. You need to reload the page to see it.

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. -->
#19320 

#### Brief description of what is fixed or changed
An object of ParametricIntegral represents integral of a scalar or vector field over a Parametric Region.

#### Other comments
`_bounds_case` function is taken from @prasoon2211  [PR](https://github.com/sympy/sympy/pull/2208/files) with some modification. I have added him as a coauthor for the corresponding commit. 
#### Release Notes

<!-- Write the release notes for this release below. 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 -->
* vector
    * added class to represent integral of scalar/vector field over a parametric surface.
<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

@friyaz friyaz marked this pull request as draft June 11, 2020 20:54
@sympy-bot
Copy link

sympy-bot commented Jun 11, 2020

🟠

Hi, I am the SymPy bot (v160). I've noticed that some of your commits add or delete files. Since this is sometimes done unintentionally, I wanted to alert you about it.

This is an experimental feature of SymPy Bot. If you have any feedback on it, please comment at sympy/sympy-bot#75.

The following commits add new files:

  • 59a69c9:
    • sympy/vector/integrals.py
    • sympy/vector/tests/test_integrals.py

If these files were added/deleted on purpose, you can ignore this message.

sympy/vector/integrals.py Outdated Show resolved Hide resolved
sympy/vector/integrals.py Outdated Show resolved Hide resolved
sympy/vector/integrals.py Outdated Show resolved Hide resolved
if len(coord_sys) > 1:
raise ValueError

coord_sys = next(iter(coord_sys))
Copy link
Member Author

@friyaz friyaz Jun 13, 2020

Choose a reason for hiding this comment

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

Since no coordinate system is associated with the parametric region, we use the base scalars of the field. But if the field does not have any basescalar or vector, we cannot derive coordinate system. This breaks the logic

Example:

>>> p = ParametricRegion(t, (3*t - 2, t + 1), {t: (1, 2)}).
>>> I = ParametricIntegral(C.x + C.y, p)
>>> I.eval()
5*sqrt(10)
>>>
>>> I = ParametricIntegral(1, p) ##We cannot extract coordinate system from 1
StopIteration

Copy link
Member

Choose a reason for hiding this comment

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

My opinion is that we should equip base scalars and base vector (fields) to the ParametricRegion if we want to use its intrinsic scalars and vector field components.
I don't think that it makes much sense to have vector integral like ParametricIntegral(C.x*C.i, p) where C.x is linked to the base scalars of the parametric region and C.i is linked to embedding cartesian coordinate system.

Copy link
Member Author

@friyaz friyaz Jun 14, 2020

Choose a reason for hiding this comment

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

Agreed. But if we allow the coordsys parameter to be optional and None by default in ParametricRegion class, a similar situation can arise.

Copy link
Member Author

Choose a reason for hiding this comment

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

@Upabjojr, your thoughts on this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably it's better to separate vector and parametric integrals. At least for now.

Copy link
Member Author

@friyaz friyaz Jun 23, 2020

Choose a reason for hiding this comment

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

The point of having subclasses is that the result of the vector integration may be a vector

I do not see how this is possible. Can you give an example?

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 think this is possible if the user want to perform cross product of vector field with normal vector instead of the usual dot product.

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, integrating velocity over time is a simple example. Though it's probably already handled by the current Integral class. But you see, this case could be a case where you may want to subclass Integral in order to supply it with the vector methods.

Copy link
Contributor

@Upabjojr Upabjojr Jun 24, 2020

Choose a reason for hiding this comment

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

But indeed, probably we don't need this case, it's pretty rare that some users may need to use it.

Copy link
Member Author

@friyaz friyaz Jun 25, 2020

Choose a reason for hiding this comment

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

Although it is a kind of vector integral, it is not integrating a vector over a region. As you said, the integrate function is able to handle such kind of integrals (integral over variables). But if we wish to handle such a case, we can allow passing a tuple of variables in place of the parametric region.

@friyaz friyaz requested a review from Upabjojr June 15, 2020 12:19
(v, lower_v, upper_v))
else:
result = integrate(parametricfield*normal_vector.magnitude(), (u, lower_u, upper_u), (v, lower_v, upper_v))

Copy link
Member Author

@friyaz friyaz Jun 15, 2020

Choose a reason for hiding this comment

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

Changing the order of parameters cuases Integral to fail or get slow.

>>> field = sqrt(C.x**2 + C.y**2)
>>> cone1 = ParametricRegion((u,v), ((2-2*u/3)*cos(v), (2-2*u/3)*sin(v), u), {u: (0, 3), v: (0, 2*pi)})
>>> cone2 = ParametricRegion((u,v), ((2-2*u/3)*cos(v), (2-2*u/3)*sin(v), u), {u: (0, 3), v: (0, 2*pi)})
>>> ParametricIntegral(field, cone1)
# It gets stuck
>>> ParametricIntegral(field, cone2)
8*sqrt(13)*pi/3

I think this is because normal_vector and normal_vector.magnitude becomes a large expression and Integral is not able to work on it. Using simplify on normal_vector.magnitude solves the problem.

Copy link
Contributor

Choose a reason for hiding this comment

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

simplify is very expensive in computational terms. What about some more specific functions (e.g. expand)?

Copy link
Member Author

Choose a reason for hiding this comment

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

@Upabjojr It gets slow if I use expand.

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 think Integrate function should be able to handle or perform such simplification instead of simplifying them here.

# Case 1 : Both bounds are constants : can perform integration in any order
# Case 2 : Bounds for u are in terms of v : integrate wrt to u first
# Case 3 : Bounds for v are in terms of u : integrate wrt to v first

Copy link
Member Author

Choose a reason for hiding this comment

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

Determining order for integration is necessary when bounds of parameters are interdependent.
Suppose we need to calculate area of a triangle:

>>> integrate(1, (x, 0, 2), (y, 10 - 5*x))
20 - 10*x
>>> integrate(1, (y, 0, 10 - 5*x), (x, 0, 2))
10 # correct answer

Copy link
Contributor

Choose a reason for hiding this comment

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

Topological sort?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we need to perform a topological sort.
This function generates the graph and performs topological sort. It works on any number of parameters.

    @classmethod             
    def _topological_sort_parameters(cls, parameters, limits):
    
        V = list(parameters)
        E = list()
        
        for p in parameters:
            lower_p = limits[p][0]
            upper_p = limits[p][1]
            
            lower_p = lower_p.atoms()
            upper_p = upper_p.atoms()
            for q in parameters:
                if p == q:
                    continue
                if lower_p.issuperset(set([q])) or upper_p.issuperset(set([q])):
                    E.append((p, q))   
        return topological_sort((V, E), key=default_sort_key)   

The only problem with this approach is when bounds are independent of each other, we can perform integration in any order. So if one order fails to calculate the integral, we can reverse the order and perform again. With my previous approach using cases, we can determine whether bounds are independent of each other but it works only for two parameters.

Copy link
Contributor

Choose a reason for hiding this comment

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

What about calling the equation solver? The equation solver should be able to find a parametrization somehow.


lower, upper = parametricregion.limits[parameter][0], parametricregion.limits[parameter][1]

if isinstance(parametricfield, Vector):
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this case should be moved to another class, e.g. ParametricVectorField?

@Upabjojr
Copy link
Contributor

Tests are broken.

@friyaz
Copy link
Member Author

friyaz commented Jun 25, 2020

@Upabjojr I have fixed them.
Also, I have added vector_integrate function. I will add the test cases for it in the next PR if it is decided to keep this function.

@codecov
Copy link

codecov bot commented Jun 25, 2020

Codecov Report

Merging #19539 into master will decrease coverage by 0.018%.
The diff coverage is 84.810%.

@@              Coverage Diff              @@
##            master    #19539       +/-   ##
=============================================
- Coverage   75.675%   75.656%   -0.019%     
=============================================
  Files          654       658        +4     
  Lines       169943    170778      +835     
  Branches     40064     40289      +225     
=============================================
+ Hits        128605    129205      +600     
- Misses       35721     35917      +196     
- Partials      5617      5656       +39     

@friyaz friyaz marked this pull request as ready for review June 25, 2020 19:00
@Upabjojr Upabjojr merged commit 200788d into sympy:master Jun 25, 2020
@Upabjojr
Copy link
Contributor

It works for me. Thanks.

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.

4 participants