refactor euler solvers#21692
Conversation
|
✅ Hi, I am the SymPy bot (v161). I'm here to help you write a release notes entry. Please read the guide on how to write release notes.
Click here to see the pull request description that was parsed. |
| def test_undetermined_coefficients_match(): | ||
| assert _undetermined_coefficients_match(g(x), x) == {'test': False} | ||
| assert _undetermined_coefficients_match(sin(2*x + sqrt(5)), x) == \ | ||
| F = lambda eq: NthLinearConstantCoeffUndeterminedCoefficients(SingleODEProblem(eq, f(x), x))._undetermined_coefficients_match(eq,x) |
There was a problem hiding this comment.
It's helpful during refactoring if the tests are changed as little as possible so we can be clear that the mostly unchanged tests still pass. This could be:
def _undetermined_coefficients_match(eq, x):
problem = SingleODEProblem(eq, f(x), x)
solver = NthLinearConstantCoeffUndeterminedCoefficients(problem)
return solver. _undetermined_coefficients_match(eq, x)
Then the rest of the testing code would not need to be changed.
| >>> f = Function('f') | ||
| >>> eq = 9*x*exp(x) + exp(-x) | ||
| >>> obj = NthLinearConstantCoeffUndeterminedCoefficients(SingleODEProblem(eq,f(x),x)) | ||
| >>> obj._undetermined_coefficients_match(eq,x) |
There was a problem hiding this comment.
There should be a space after a comma (here and everywhere else)
| >>> from sympy import log, exp | ||
| >>> from sympy.solvers.ode.ode import _undetermined_coefficients_match | ||
| >>> from sympy import log, exp, Function | ||
| >>> from sympy.solvers.ode.single import NthLinearConstantCoeffUndeterminedCoefficients,SingleODEProblem |
There was a problem hiding this comment.
This line is too long
| >>> eq = log(x) | ||
| >>> obj = NthLinearConstantCoeffUndeterminedCoefficients(SingleODEProblem(eq,f(x),x)) | ||
| >>> obj._undetermined_coefficients_match(eq,x) | ||
| {'test': False} |
There was a problem hiding this comment.
It's probably better to give an example using dsolve here.
There was a problem hiding this comment.
This docstring is just for getting trial set method. For examples of undet_coeff solver the docstring at class level has examples with dsolve.
| sol = collect(sol, x**i*exp(reroot*x)) | ||
| sol = powsimp(sol) | ||
| return [Eq(f(x), sol)] | ||
| return Eq(f(x), sol) |
There was a problem hiding this comment.
Returning anything other than a list should fail here. Is there some code somewhere else that checks for this? Why didn't this fail before?
There was a problem hiding this comment.
Currently test_single.py handles both type of solutions with list as well as without as some of the solvers are yet to be refactored. After refactoring I will remove the conditional part from test suite so that it should expect list of solution from every solver.
| return gensols | ||
|
|
||
|
|
||
| def _get_general_solution(self, *, simplify_flag: bool = True): |
There was a problem hiding this comment.
Only one blank line here
|
|
||
| def _get_general_solution(self, *, simplify_flag: bool = True): | ||
| self._get_sols(self.r) | ||
| return [self.gsol] |
There was a problem hiding this comment.
Shouldn't this just be return self._get_sols()?
It would be better if the methods in these classes don't use internal variables like this. We should minimise instance attributes.
Why is _get_sols a different method from _get_general_solution?
There was a problem hiding this comment.
_get_sols() is the method which calcutates the roots of characteristic equation. And to form that we need match_object.
self.r was kept as an argument because this method is also used by euler variation of parameters and euler undetermined coeffs so we need to pass their match object to this.
There was a problem hiding this comment.
Rather than passing the match object it would be better if this had names arguments like roots, trialset or something.
There was a problem hiding this comment.
Why can't the vop and undet classes just use the euler class get_general_solution method?
There was a problem hiding this comment.
Why can't the vop and undet classes just use the euler class
get_general_solutionmethod?
because get_general_solution will return a equation where as in vop and undet we just want the list of roots of characterstic equation.
| chareq, symbol = S.Zero, Dummy('x') | ||
|
|
||
| for i in r.keys(): | ||
| if not isinstance(i, str) and i >= 0: |
There was a problem hiding this comment.
If r is a dict then it isn't necessary to call keys here (the dict is already iterable).
Why would i be a str?
There was a problem hiding this comment.
r can have key named 'trialset' which will be used for undetermined coefficient.
There was a problem hiding this comment.
It would be better to store trialset as a separate attribute rather than mix it up with the roots:
self.roots = ...
self.trialset = ...
|
Side note: these Euler solvers take up 400 lines that would be reduced to almost nothing if the ODE was simply solved by substitution. |
| fx = self.ode_problem.func | ||
| x = self.ode_problem.sym | ||
| (C1,) = self.ode_problem.get_numbered_constants(num=1) | ||
| (C1, ) = self.ode_problem.get_numbered_constants(num=1) |
There was a problem hiding this comment.
This space should not be here
|
|
||
| These kinds of differential equations can be solved in a general way. The | ||
| integrating factor `e^{\int P(x) \,dx}` will turn the equation into a | ||
| integrating factor `e^{\int P(x) \, dx}` will turn the equation into a |
There was a problem hiding this comment.
The \, in latex is different from other commas since it represents a small space. I don't think that we need to also add a space after this.
|
|
||
| .. math:: y' + F\left(\!\frac{a_1 x + b_1 y + c_1}{a_2 x + b_2 y + | ||
| c_2}\!\right) = 0\text{,} | ||
| c_2}\!\right) = 0\text{, } |
There was a problem hiding this comment.
This space should not be here
|
It looks like you've done a find and replace to add spaces after commas but some of these places shouldn't be changed so can you check through those? |
Yes I did that. I will check once. Also I am trying to move all the reusable functions to another file so that classes look minimal and kind of wrapper for them. |
🟠Hi, I am the SymPy bot (v161). 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:
If these files were added/deleted on purpose, you can ignore this message. |
|
@oscarbenjamin does this way seem good? |
Yes, I think so. I think it should just be called |
So should I again move functions like Currently all the functions which are there in new file they all are used by more than 1 solver. If we again keep any of them into solver, we will have to create instance of that class into another. So what all functions should I keep in classes back? |
| gensols.append(sin_form) | ||
| return gsol, gensols | ||
|
|
||
| def _solve_variation_of_parameters(eq, func, roots, homogen_sol, order, match_obj, simplify_flag=True): |
| psol = trigsimp(psol, deep=True) | ||
| return Eq(f(x), homogen_sol.rhs + psol) | ||
|
|
||
| def _get_const_characterstic_eq_sols(r, func, order): |
No I think this is fine. Eventually these functions should be made into something that it is more usefully reusable though. |
| @@ -0,0 +1,463 @@ | |||
| from collections import defaultdict | |||
There was a problem hiding this comment.
There should be some kind of comment or module level docstring at the top of the module explaining what it is.
| return False | ||
|
|
||
|
|
||
| def _get_euler_characterstic_eq_sols(eq, func, match_obj): |
There was a problem hiding this comment.
The functions in this file should have at least a one line docstring explaining what they are as well as comments explaining why they are here and what they are used for.
| def _get_euler_characteristic_eq_sols(eq, func, match_obj): | ||
| r""" | ||
| It returns the solution of homogeneous part of the linear euler ODE and | ||
| the list of roots of characteristic equation. |
There was a problem hiding this comment.
Don't use "It" here. Just "Returns ..." is fine.
This doesn't really explain the function very well. What is the match_obj parameter supposed to be for example?
| Functions that are for internal use: | ||
|
|
||
| 1) _test_term(coeff, func, order) - It takes coefficient of partiular term, function and | ||
| order to which it is associated. It returns whether given term matches with linear euler ODEs. |
There was a problem hiding this comment.
I don't think that you need to detail each function at the top here. These kinds of comments/description are better placed in or immediately before the function.
This module level docstring just needs to explain the general purpose of the module.
| def _wilds(self, f, x, order): | ||
| fy = Wild('fy', exclude=[0, f(x).diff(x), f(x).diff(x, 2)]) | ||
| return (fy,) | ||
| return (fy, ) |
There was a problem hiding this comment.
There shouldn't be a space here
There was a problem hiding this comment.
Okay, will remove it in next PR.
|
Okay, looks good. |
References to other Issues or PRs
SEE #18348
Brief description of what is fixed or changed
Refactor Euler solvers and cleanup the constant coff code from ode.py
Other comments
Release Notes
NO ENTRY