It's not clear that there is anything you want to discuss, and you certainly don't ask any questions. What I would like to see is a way for the user to request DSolve[]
to work harder, in the way we can use Reduce[]
instead of Solve[]
, or use MaxExtraConditions
in Solve[]
. I think there's great value in DSolve[]
being quick and generic (okay, "quick" as a design goal, even if it sometimes takes forever). But it's also the case the Mathematica can give a more robust solution in some cases, if the user is willing to let Mathematica perform operations that risk being computationally intensive.
For instance DSolve[{y'[x] == Sin[y[x]], y[0] == y0}, y, x]
returns only two one-parameter families of solutions. But the ODE y'[x] == Sin[y[x]]
admits the symmetries generated by {{y -> -y}, {y -> Pi-y}, {x -> x+C[1]}}
. So there are infinitely many one-parameter families of solutions.
For instance, the following hacky way culls the implicit solution before it's solved, so that we can get the complete solution, which DSolve[]
does not do on its own:
ode = y'[x] == Sin[y[x]];
implSOL = Trace[
DSolve[{ode, y[0] == y0}, y, x]
, s : HoldPattern[Solve[_, y[x], ___]] :>
Return[Inactivate[s, Solve], Trace]
, TraceInternal -> True]
dsol2 = Activate[implSOL] // DSolve`DSolveToPureFunction
ode /. % // FullSimplify
(*
Solve[-ArcTanh[Cos[y[x]]]\[Equal]x+C[1],y[x]]
{{y -> Function[{x},ConditionalExpression[-ArcCos[-Tanh[x+C[1]]]+2 \[Pi] C[2], <<>>]]},
{y -> Function[{x},ConditionalExpression[ArcCos[-Tanh[x+C[1]]]+2 \[Pi] C[2],<<>>]]}}
{ConditionalExpression[True,<<>>], ConditionalExpression[True,<<>>]}
*)
If we add assumptions that only a single one-parameter family satisfies, DSolve[y'[x] == Sin[y[x]], y, x,
Assumptions -> 0 < y[x] < Pi && {x, C[1]} \[Element] Reals]
still returns two families, one of which satisfies the assumptions. This does not bother me in one way, because I appreciate the difference between "if
$P$ then
$Q$" (
$P$ is an assumption) and "$P$ and
$Q$" (
$P$ is a constraint). In another way, it bothers me that I can't add a constraint to the solution. So here is another hacky way in which we treat $Assumptions
as constraints when Solve[]
is called by DSolve[]
:
Internal`InheritedBlock[{Solve},
Unprotect[Solve];
Solve[eq_, v_, opts___] /; ! TrueQ[$in] :=
Block[{$in = True},
Simplify@Solve[Flatten@{eq, $Assumptions}, v, opts]];
Protect[Solve];
DSolve[y'[x] == Sin[y[x]], y, x,
Assumptions -> 0 < y[x] < Pi && {x, C[1]} \[Element] Reals]]
(* {{y -> Function[{x}, ArcCos[-Tanh[x + C[1]]]]}} *)
There is no problem, theoretically, solving any IVP for this ODE. But DSolve[]
fails if the initial condition cannot be satisfied by one of the two one-parameter familiar in its "general solution":
DSolve[{y'[x] == Sin[y[x]], y[0] == 3 Pi/2}, y, x,
Assumptions -> 0 < y[x] < Pi && {x, C[1]} \[Element] Reals]
(* DSolve::bvnul, DSolve::ifun errors *)
(* {} *)
But if I add constraints to Solve[]
as above, then Solve[]
will find the right branch of the general solution for the IVP:
Internal`InheritedBlock[{Solve},
Unprotect[Solve];
Solve[eq_, v_, opts___] /; ! TrueQ[$in] :=
Block[{$in = True},
Simplify@Solve[Flatten@{eq, $Assumptions}, v, opts]];
Protect[Solve];
DSolve[{y'[x] == Sin[y[x]], y[0] == 3 Pi/2}, y, x,
Assumptions -> Pi < y[x] < 2 Pi && {x, C[1]} \[Element] Reals]]
(* {{y -> Function[{x}, 2 \[Pi] - ArcCos[-Tanh[x]]]}} *)
Indeed, we can find the general solution for this range of y
, a solution that is not contained in the "general solution" returned by DSolve[{y'[x] == Sin[y[x]], y[0] == y0}, y, x]
:
Internal`InheritedBlock[{Solve},
Unprotect[Solve];
Solve[eq_, v_, opts___] /; ! TrueQ[$in] :=
Block[{$in = True},
Simplify@Solve[Flatten@{eq, $Assumptions}, v, opts]];
Protect[Solve];
DSolve[y'[x] == Sin[y[x]], y, x,
Assumptions -> Pi < y[x] < 2 Pi && {x, C[1]} \[Element] Reals]]
(* {{y -> Function[{x}, 2 \[Pi] - ArcCos[-Tanh[x + C[1]]]]}} *)