1- export PolyForm, simplify_fractions, quick_cancel
1+ export PolyForm, simplify_fractions, quick_cancel, flatten_fractions
22using Bijections
33using DynamicPolynomials: PolyVar
44
@@ -7,7 +7,8 @@ using DynamicPolynomials: PolyVar
77
88Abstracts a [MultivariatePolynomials.jl](https://juliaalgebra.github.io/MultivariatePolynomials.jl/stable/) as a SymbolicUtils expression and vice-versa.
99
10- The SymbolicUtils term interface (`istree`, `operation, and `arguments`) works on PolyForm lazily: the `operation` and `arguments` are created by converting one level of arguments into SymbolicUtils expressions. They may further contain PolyForm within them.
10+ The SymbolicUtils term interface (`istree`, `operation, and `arguments`) works on PolyForm lazily:
11+ the `operation` and `arguments` are created by converting one level of arguments into SymbolicUtils expressions. They may further contain PolyForm within them.
1112We use this to hold polynomials in memory while doing `simplify_fractions`.
1213
1314 PolyForm{T}(x; Fs=Union{typeof(*),typeof(+),typeof(^)}, recurse=false)
@@ -190,15 +191,16 @@ function TermInterface.arguments(x::PolyForm{T}) where {T}
190191
191192 if MP. nterms (x. p) == 1
192193 MP. isconstant (x. p) && return [convert (Number, x. p)]
193- c = MP. coefficient (x. p)
194- t = MP. monomial (x. p)
194+ t = MP. term (x. p)
195+ c = MP. coefficient (t)
196+ m = MP. monomial (t)
195197
196198 if ! isone (c)
197199 [c, (unstable_pow (resolve (v), pow)
198- for (v, pow) in MP. powers (t ) if ! iszero (pow)). .. ]
200+ for (v, pow) in MP. powers (m ) if ! iszero (pow)). .. ]
199201 else
200202 [unstable_pow (resolve (v), pow)
201- for (v, pow) in MP. powers (t ) if ! iszero (pow)]
203+ for (v, pow) in MP. powers (m ) if ! iszero (pow)]
202204 end
203205 else
204206 ts = MP. terms (x. p)
@@ -223,12 +225,12 @@ Expand expressions by distributing multiplication over addition, e.g.,
223225multivariate polynomials implementation.
224226`variable_type` can be any subtype of `MultivariatePolynomials.AbstractVariable`.
225227"""
226- expand (expr) = PolyForm (expr, Fs= Union{typeof (+ ), typeof (* ), typeof (^ )}, recurse= true )
228+ expand (expr) = Postwalk (identity)( PolyForm (expr, Fs= Union{typeof (+ ), typeof (* ), typeof (^ )}, recurse= true ) )
227229
228230
229231# # Rational Polynomial form with Div
230232
231- function polyform_factors (d:: Div , pvar2sym, sym2term)
233+ function polyform_factors (d, pvar2sym, sym2term)
232234 make (xs) = map (xs) do x
233235 if x isa Pow && x. base isa Integer && x. exp > 0
234236 # here we do want to recurse one level, that's why it's wrong to just
@@ -255,11 +257,11 @@ function simplify_div(d::Div)
255257 end
256258end
257259
258- function add_divs (x:: Div , y:: Div )
260+ function add_divs (x, y)
259261 x_num, x_den = polyform_factors (x, get_pvar2sym (), get_sym2term ())
260262 y_num, y_den = polyform_factors (y, get_pvar2sym (), get_sym2term ())
261263
262- Div (_mul (x_num, y_den) + _mul (x_den, y_num), _mul (x_den, y_den))
264+ (_mul (x_num, y_den) + _mul (x_den, y_num)) / ( _mul (x_den, y_den))
263265end
264266
265267"""
@@ -278,9 +280,38 @@ function simplify_fractions(x)
278280 rules = [@rule ~ x:: isdiv => simplify_div (~ x)
279281 @acrule ~ a:: isdiv + ~ b:: isdiv => add_divs (~ a,~ b)]
280282
283+ Fixpoint (Postwalk (RestartedChain (rules)))(x)
284+ end
285+
286+ """
287+ flatten_fractions(x)
288+
289+ Flatten nested fractions that are added together.
290+
291+ ```julia
292+ julia> flatten_fractions((1+(1+1/a)/a)/a)
293+ (1 + a + a^2) / (a^3)
294+ ```
295+ """
296+ function flatten_fractions (x)
297+ rules = [@acrule ~ a:: (x->x isa Div) + ~ b => add_divs (~ a,~ b)
298+ @rule * (~~ x, ~ a / ~ b, ~~ y) / ~ c => * ((~~ x). .. , ~ a, (~~ y). .. ) / (~ b * ~ c)
299+ @rule ~ c / * (~~ x, ~ a / ~ b, ~~ y) => (~ b * ~ c) / * ((~~ x). .. , ~ a, (~~ y). .. )]
281300 Fixpoint (Postwalk (Chain (rules)))(x)
282301end
283302
303+ function fraction_iszero (x)
304+ ! istree (x) && return _iszero (x)
305+ # fast path and then slow path
306+ any (_iszero, numerators (flatten_fractions (x))) ||
307+ any (_iszero∘ expand, numerators (flatten_fractions (x)))
308+ end
309+
310+ function fraction_isone (x)
311+ ! istree (x) && return _isone (x)
312+ _isone (simplify_fractions (flatten_fractions (x)))
313+ end
314+
284315function needs_div_rules (x)
285316 (x isa Div && ! (x. num isa Number) && ! (x. den isa Number)) ||
286317 (istree (x) && operation (x) === (+ ) && count (has_div, unsorted_arguments (x)) > 1 ) ||
0 commit comments