DSolve skips restrictions when expressing the solution from the first integral

I consider the ODE: $$y' =-\frac{y^2+1}{x^2+1},$$ and DSolve

DSolve[{y'[x] (x^2 + 1) == -(y[x]^2 + 1)}, y[x], x,
Assumptions -> Element[x, Reals]]


gives the solution $$y=y(x)$$:

{{y[x] -> -Tan[ArcTan[x] - C[1]]}}.


But, if I solve the equation by the separable variable method, then I get the general solution in the form $$\arctan(y(x))+\arctan(x)=C.$$ I can express the solution from this equation in the form of DSolve answer, but I have restriction $$- \pi/2 < C-\arctan(x) < \pi/2$$. Of course, Reduce gives this resuts:

Reduce[ArcTan[y] + ArcTan[x] == c, y, Reals]

1/2 (2 c - Pi) < ArcTan[x] < 1/2 (2 c + Pi) &&
y == Tan[c - ArcTan[x]]


but DSolve does not show it.

What is the reason for this? Is there any possibility to influence on DSolve?

• Your $C$ and the DSolve constant C[1] are distinct, the connection being that their difference is a multiple of $\pi$. The DSolve answer gives a solution for all C[1], so I'm not sure how you can get DSolve to do it your way. This brings out the restriction in terms of the multiple of $\pi$: First@Solve[Equal @@@ #, {C[1], y[x]}, Reals] & /@ DSolve[{y'[x] (x^2 + 1) == -(y[x]^2 + 1)}, y[x], x] Aug 27, 2021 at 12:51
• No, my $C$ and C[1] from DSolve are equal. It follows from $\arctan(y(x))+\arctan(x)=C$: $y(x) = \tan(C-\arctan(x))$. But if we consider the solution denends on the real argument $x$, this formula is true under the restriction on $x$ which I wrote above. Otherwise, the uniqueness of the solution may not be achieved. Aug 27, 2021 at 14:35

Either:

InternalInheritedBlock[{Solve},
Unprotect[Solve];
Solve[eq_, y[x]] := Solve[eq, y[x], Reals];
Protect[Solve];
DSolve[{y'[x] (x^2 + 1) == -(y[x]^2 + 1)}, y[x], x]
]


Or:

(* Compare https://mathematica.stackexchange.com/a/174467 *)
Trace[
DSolve[{y'[x] (x^2 + 1) == -(y[x]^2 + 1)}, y[x], x],
Solve[implicit_, y[x]] :> Return[Solve[implicit, y[x], Reals], Trace],
TraceInternal -> True]


Return this:

(*
{{y[x] -> ConditionalExpression[
-Tan[ArcTan[x] - C[1]],
(1/2)*(-Pi + 2*ArcTan[x]) < C[1] < (1/2)*(Pi + 2*ArcTan[x])]}}
*)


And an IVP usually gets you proper dependence on parameters, though IVPs are sometimes harder to solve:

DSolve[{y'[x] (x^2 + 1) == -(y[x]^2 + 1), y[0] == c}, y[x], x]


Solve::ifun: Inverse functions are being used....

(*  {{y[x] -> Tan[ArcTan[c] - ArcTan[x]]}}  *)


Notes:

1. DSolve does not attempt to produce formulas that are 1-1/injective mappings from a parameter domain (say, for the integration constants) to the solution space. (The mappings are not always "onto"/surjective either.) When the formulas involve transcendental functions, you're rather lucky to get formulas straight out of DSolve that satisfy the desire for "uniqueness," which was a condition added by the OP in a comment.

2. Assumptions are tricky. Logically, Assumptions -> P should be like Implies[P, Q] and not And[P, Q]. Assumptions sets conditions that may be assumed when useful, but otherwise are not necessary. That is to say, if you want to impose constraints, using Assumptions may or may not have the intended effect. So the use of Assumptions -> Element[x, Reals] in DSolve will not force the solution(s) to be real-only. OTOH, Solve[..., Reals] and Reduce[..., Reals] make real-only a constraint. However, in the first two codes, the Reals constraint applies only to the final solution step, not the intermediate steps used by DSolve.

3. The first code is probably better: If DSolve[sys, y, x], it return a function y -> Function[{x}, sol]; the second returns y[x] -> sol no matter the form of the DSolve` call.