@@ -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