@@ -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+ end
98+ # if the denominator isn't a polynomial in `wrt`, better to not include it
99+ # to reduce the size of the gcd polynomial
100+ if ! contains_variable (den, wrt)
101+ return num / den, 1
102+ end
103+ return num, den
104+ end
105+
106+ """
107+ $(TYPEDSIGNATURES)
108+
60109Convert `expr` from a symbolics expression to one that uses `HomotopyContinuation.ModelKit`.
61110"""
62111function symbolics_to_hc (expr)
@@ -139,51 +188,74 @@ function MTK.HomotopyContinuationProblem(
139188 dvs = unknowns (sys)
140189 eqs = equations (sys)
141190
142- for eq in eqs
191+ denoms = []
192+ eqs2 = map (eqs) do eq
143193 if ! is_polynomial (eq. lhs, dvs) || ! is_polynomial (eq. rhs, dvs)
144194 error (" Equation $eq is not a polynomial in the unknowns. See warnings for further details." )
145195 end
196+ num, den = handle_rational_polynomials (eq. rhs - eq. lhs, dvs)
197+ push! (denoms, den)
198+ return 0 ~ num
146199 end
147200
148- nlfn, u0, p = MTK. process_SciMLProblem (NonlinearFunction{true }, sys, u0map, parammap;
201+ sys2 = MTK. @set sys. eqs = eqs2
202+
203+ nlfn, u0, p = MTK. process_SciMLProblem (NonlinearFunction{true }, sys2, u0map, parammap;
149204 jac = true , eval_expression, eval_module)
150205
206+ denominator = MTK. build_explicit_observed_function (sys, denoms)
207+
151208 hvars = symbolics_to_hc .(dvs)
152209 mtkhsys = MTKHomotopySystem (nlfn. f, p, nlfn. jac, hvars, length (eqs))
153210
154211 obsfn = MTK. ObservedFunctionCache (sys; eval_expression, eval_module)
155212
156- return MTK. HomotopyContinuationProblem (u0, mtkhsys, sys, obsfn)
213+ return MTK. HomotopyContinuationProblem (u0, mtkhsys, denominator, sys, obsfn)
157214end
158215
159216"""
160217$(TYPEDSIGNATURES)
161218
162219Solve 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`.
220+ uses `HomotopyContinuation.jl`. All keyword arguments except the ones listed below are
221+ forwarded to `HomotopyContinuation.solve`. The original solution as returned by
222+ `HomotopyContinuation.jl` will be available in the `.original` field of the returned
223+ `NonlinearSolution`.
166224
167225All keyword arguments have their default values in HomotopyContinuation.jl, except
168226`show_progress` which defaults to `false`.
227+
228+ Extra keyword arguments:
229+ - `denominator_abstol`: In case `prob` is solving a rational function, roots which cause
230+ the denominator to be below `denominator_abstol` will be discarded.
169231"""
170232function CommonSolve. solve (prob:: MTK.HomotopyContinuationProblem ,
171- alg = nothing ; show_progress = false , kwargs... )
233+ alg = nothing ; show_progress = false , denominator_abstol = 1e-8 , kwargs... )
172234 sol = HomotopyContinuation. solve (
173235 prob. homotopy_continuation_system; show_progress, kwargs... )
174236 realsols = HomotopyContinuation. results (sol; only_real = true )
175237 if isempty (realsols)
176238 u = state_values (prob)
177- resid = prob. homotopy_continuation_system (u)
178239 retcode = SciMLBase. ReturnCode. ConvergenceFailure
179240 else
241+ T = eltype (state_values (prob))
180242 distance, idx = findmin (realsols) do result
243+ if any (<= (denominator_abstol),
244+ prob. denominator (real .(result. solution), parameter_values (prob)))
245+ return T (Inf )
246+ end
181247 norm (result. solution - state_values (prob))
182248 end
183- u = real .(realsols[idx]. solution)
184- resid = prob. homotopy_continuation_system (u)
185- retcode = SciMLBase. ReturnCode. Success
249+ # all roots cause denominator to be zero
250+ if isinf (distance)
251+ u = state_values (prob)
252+ retcode = SciMLBase. ReturnCode. Infeasible
253+ else
254+ u = real .(realsols[idx]. solution)
255+ retcode = SciMLBase. ReturnCode. Success
256+ end
186257 end
258+ resid = prob. homotopy_continuation_system (u)
187259
188260 return SciMLBase. build_solution (
189261 prob, :HomotopyContinuation , u, resid; retcode, original = sol)
0 commit comments