@@ -2,7 +2,7 @@ module MTKHomotopyContinuationExt
22
33using ModelingToolkit
44using ModelingToolkit. SciMLBase
5- using ModelingToolkit. Symbolics: unwrap, symtype
5+ using ModelingToolkit. Symbolics: unwrap, symtype, BasicSymbolic, simplify_fractions
66using ModelingToolkit. SymbolicIndexingInterface
77using ModelingToolkit. DocStringExtensions
88using HomotopyContinuation
@@ -27,7 +27,7 @@ function is_polynomial(x, wrt)
2727 contains_variable (x, wrt) || return true
2828 any (isequal (x), wrt) && return true
2929
30- if operation (x) in (* , + , - )
30+ if operation (x) in (* , + , - , / )
3131 return all (y -> is_polynomial (y, wrt), arguments (x))
3232 end
3333 if operation (x) == (^ )
5757"""
5858$(TYPEDSIGNATURES)
5959
60+ Given a `x`, a polynomial in variables in `wrt` which may contain rational functions,
61+ express `x` as a single rational function with polynomial `num` and denominator `den`.
62+ Return `(num, den)`.
63+ """
64+ function handle_rational_polynomials (x, wrt)
65+ x = unwrap (x)
66+ symbolic_type (x) == NotSymbolic () && return x, 1
67+ iscall (x) || return x, 1
68+ contains_variable (x, wrt) || return x, 1
69+ any (isequal (x), wrt) && return x, 1
70+
71+ # simplify_fractions cancels out some common factors
72+ # and expands (a / b)^c to a^c / b^c, so we only need
73+ # to handle these cases
74+ x = simplify_fractions (x)
75+ op = operation (x)
76+ args = arguments (x)
77+
78+ if op == /
79+ # numerator and denominator are trivial
80+ num, den = args
81+ # but also search for rational functions in numerator
82+ n, d = handle_rational_polynomials (num, wrt)
83+ num, den = n, den * d
84+ elseif op == +
85+ num = 0
86+ den = 1
87+
88+ # we don't need to do common denominator
89+ # because we don't care about cases where denominator
90+ # is zero. The expression is zero when all the numerators
91+ # are zero.
92+ for arg in args
93+ n, d = handle_rational_polynomials (arg, wrt)
94+ num += n
95+ den *= d
96+ end
97+ else
98+ return x, 1
99+ end
100+ # if the denominator isn't a polynomial in `wrt`, better to not include it
101+ # to reduce the size of the gcd polynomial
102+ if ! contains_variable (den, wrt)
103+ return num / den, 1
104+ end
105+ return num, den
106+ end
107+
108+ """
109+ $(TYPEDSIGNATURES)
110+
60111Convert `expr` from a symbolics expression to one that uses `HomotopyContinuation.ModelKit`.
61112"""
62113function symbolics_to_hc (expr)
@@ -139,51 +190,74 @@ function MTK.HomotopyContinuationProblem(
139190 dvs = unknowns (sys)
140191 eqs = equations (sys)
141192
142- for eq in eqs
193+ denoms = []
194+ eqs2 = map (eqs) do eq
143195 if ! is_polynomial (eq. lhs, dvs) || ! is_polynomial (eq. rhs, dvs)
144196 error (" Equation $eq is not a polynomial in the unknowns. See warnings for further details." )
145197 end
198+ num, den = handle_rational_polynomials (eq. rhs - eq. lhs, dvs)
199+ push! (denoms, den)
200+ return 0 ~ num
146201 end
147202
148- nlfn, u0, p = MTK. process_SciMLProblem (NonlinearFunction{true }, sys, u0map, parammap;
203+ sys2 = MTK. @set sys. eqs = eqs2
204+
205+ nlfn, u0, p = MTK. process_SciMLProblem (NonlinearFunction{true }, sys2, u0map, parammap;
149206 jac = true , eval_expression, eval_module)
150207
208+ denominator = MTK. build_explicit_observed_function (sys, denoms)
209+
151210 hvars = symbolics_to_hc .(dvs)
152211 mtkhsys = MTKHomotopySystem (nlfn. f, p, nlfn. jac, hvars, length (eqs))
153212
154213 obsfn = MTK. ObservedFunctionCache (sys; eval_expression, eval_module)
155214
156- return MTK. HomotopyContinuationProblem (u0, mtkhsys, sys, obsfn)
215+ return MTK. HomotopyContinuationProblem (u0, mtkhsys, denominator, sys, obsfn)
157216end
158217
159218"""
160219$(TYPEDSIGNATURES)
161220
162221Solve a `HomotopyContinuationProblem`. Ignores the algorithm passed to it, and always
163- uses `HomotopyContinuation.jl`. All keyword arguments are forwarded to
164- `HomotopyContinuation.solve`. The original solution as returned by `HomotopyContinuation.jl`
165- will be available in the `.original` field of the returned `NonlinearSolution`.
222+ uses `HomotopyContinuation.jl`. All keyword arguments except the ones listed below are
223+ forwarded to `HomotopyContinuation.solve`. The original solution as returned by
224+ `HomotopyContinuation.jl` will be available in the `.original` field of the returned
225+ `NonlinearSolution`.
166226
167227All keyword arguments have their default values in HomotopyContinuation.jl, except
168228`show_progress` which defaults to `false`.
229+
230+ Extra keyword arguments:
231+ - `denominator_abstol`: In case `prob` is solving a rational function, roots which cause
232+ the denominator to be below `denominator_abstol` will be discarded.
169233"""
170234function CommonSolve. solve (prob:: MTK.HomotopyContinuationProblem ,
171- alg = nothing ; show_progress = false , kwargs... )
235+ alg = nothing ; show_progress = false , denominator_abstol = 1e-8 , kwargs... )
172236 sol = HomotopyContinuation. solve (
173237 prob. homotopy_continuation_system; show_progress, kwargs... )
174238 realsols = HomotopyContinuation. results (sol; only_real = true )
175239 if isempty (realsols)
176240 u = state_values (prob)
177- resid = prob. homotopy_continuation_system (u)
178241 retcode = SciMLBase. ReturnCode. ConvergenceFailure
179242 else
243+ T = eltype (state_values (prob))
180244 distance, idx = findmin (realsols) do result
245+ if any (<= (denominator_abstol),
246+ prob. denominator (real .(result. solution), parameter_values (prob)))
247+ return T (Inf )
248+ end
181249 norm (result. solution - state_values (prob))
182250 end
183- u = real .(realsols[idx]. solution)
184- resid = prob. homotopy_continuation_system (u)
185- retcode = SciMLBase. ReturnCode. Success
251+ # all roots cause denominator to be zero
252+ if isinf (distance)
253+ u = state_values (prob)
254+ retcode = SciMLBase. ReturnCode. Infeasible
255+ else
256+ u = real .(realsols[idx]. solution)
257+ retcode = SciMLBase. ReturnCode. Success
258+ end
186259 end
260+ resid = prob. homotopy_continuation_system (u)
187261
188262 return SciMLBase. build_solution (
189263 prob, :HomotopyContinuation , u, resid; retcode, original = sol)
0 commit comments