Skip to content

Commit 1f84fb8

Browse files
committed
more dev docs
1 parent fc843fb commit 1f84fb8

3 files changed

Lines changed: 93 additions & 10 deletions

File tree

benchmark/benchmarks.jl

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,7 @@ SUITE["fourier"]["trig_to_exp_product"] = @benchmarkable trig_to_exp($trig_expr_
123123
exp_sum = unwrap(exp(im * a) + exp(-im * a) + exp(im * (a + b)))
124124
SUITE["fourier"]["exp_to_trig_sum"] = @benchmarkable exp_to_trig($exp_sum)
125125

126-
SUITE["fourier"]["add_div"] = @benchmarkable add_div(
127-
$(a / (b + c) + b / (a + c))
128-
)
126+
SUITE["fourier"]["add_div"] = @benchmarkable add_div($(a / (b + c) + b / (a + c)))
129127

130128
SUITE["fourier"]["trig_reduce_simple"] = @benchmarkable trig_reduce($trig_expr_sq)
131129
SUITE["fourier"]["trig_reduce_product"] = @benchmarkable trig_reduce($trig_expr_product)
@@ -203,13 +201,12 @@ SUITE["construction"]["DifferentialEquation_coupled"] = @benchmarkable Different
203201

204202
# Rearrangement benchmark
205203
diff_eq_for_rearrange = DifferentialEquation(
206-
[d(x, t, 2) + ω0^2 * x - k * y ~ F * cos* t),
207-
d(y, t, 2) + ω0^2 * y - k * x ~ 0],
204+
[d(x, t, 2) + ω0^2 * x - k * y ~ F * cos* t), d(y, t, 2) + ω0^2 * y - k * x ~ 0],
208205
[x, y],
209206
)
210-
SUITE["construction"]["rearrange_standard"] = @benchmarkable rearrange_standard!(
211-
eom
212-
) setup = (eom = deepcopy($diff_eq_for_rearrange))
207+
SUITE["construction"]["rearrange_standard"] = @benchmarkable rearrange_standard!(eom) setup = (
208+
eom = deepcopy($diff_eq_for_rearrange)
209+
)
213210

214211
# ==========================================================================
215212
# Harmonic checks

docs/src/dev/symbolic_rewriting.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,89 @@ QuestBase's small, targeted transformations.
100100

101101
`Postwalk` is still used where it works well: `expand_all` and `add_div`, where the
102102
rewriter needs to visit all nodes anyway.
103+
104+
## Function-by-Function Analysis
105+
106+
Every function in `src/Symbolics/` was analyzed for rule-based rewriting potential.
107+
This section serves as a reference for future optimization attempts.
108+
109+
### Candidates That Were Tried (manual is faster for now)
110+
111+
| Function | `@rule` pattern | Slowdown | Notes |
112+
|---|---|---|---|
113+
| `expand_exp_power` | `exp(~x)^~n => exp(~x * ~n)` | 1.9x | Single pattern, `Postwalk` overhead dominates on small trees |
114+
| `simplify_exp_products` | `@acrule exp(~a) * exp(~b) => exp(~a + ~b)` | 6.8x | Needs `Fixpoint` + 3 chained rules; n-ary `findall` is faster |
115+
| `expand_fraction` | `(+(~~xs)) / ~c => sum(x/~c for x in ~~xs)` | 1.5x | Segment variable `~~xs` works but `_apply_termwise` is leaner |
116+
| `exp_to_trig` | Custom node function via `PassThrough` | 2500x (no-op) | `_apply_termwise` skips non-exp subtrees; `Postwalk` visits all |
117+
118+
These could become faster than manual code if expression sizes grow significantly
119+
(100+ nodes) or if SymbolicUtils optimizes the rewriter overhead in the future.
120+
121+
### Potentially Worth Revisiting
122+
123+
| Function | Possible `@rule` | Why it might help |
124+
|---|---|---|
125+
| `trig_to_exp` (both `Num` and `BasicSymbolic` versions) | `@rule sin(~x)^~n => im^n * ((exp(-im*~x) - exp(im*~x))/2)^n` and similar for `cos` | Currently builds a substitution dict manually by iterating `get_all_terms` and filtering trigs. A `Postwalk` with two rules would be more declarative. Worth trying if `trig_to_exp` becomes a bottleneck. |
126+
| `simplify_complex` | `@rule` on `Const`-wrapped complex with zero imaginary | Currently uses `_apply_termwise` + `unwrap_const`. A single rule could handle the leaf case but traversal still needed. |
127+
| `is_trig` | `@capture` macro | Could use `@capture expr (sin(~x) or cos(~x))` instead of manual `ispow` + `operation` check. Cleaner but unlikely faster. |
128+
129+
### Not Candidates (complex logic, not pattern-based)
130+
131+
| Function | File | Why rules don't fit |
132+
|---|---|---|
133+
| `get_independent` | `Symbolics_utils.jl` | Requires semantic `is_function()` check per argument — each term needs a variable-dependence query, not a structural pattern match |
134+
| `exp_to_trig` (inner logic) | `fourier.jl` | Sign normalization uses alphabetic ordering of `sorted_arguments` and `_has_negative_coefficient` heuristics — too stateful for a rule |
135+
| `drop_powers` | `drop_powers.jl` | Uses algebraic ε-substitution strategy: replaces `var → ε*var`, expands, removes high powers of ε. Not tree-pattern-based at all |
136+
| `_fourier_term` | `fourier.jl` | Multi-step pipeline: `trig_reduce``get_independent` → complex simplification. Orchestration logic, not a rewrite |
137+
| `_get_all_terms` | `Symbolics_utils.jl` | Collects terms into a flat list — accumulation, not transformation |
138+
| `power_of` / `max_power` | `drop_powers.jl` | Query functions returning integers, not expression rewrites |
139+
| `count_derivatives` | `Symbolics_utils.jl` | Single-node field access (`op.order`), not a traversal |
140+
| `is_harmonic` | `Symbolics_utils.jl` | Predicate combining `get_all_terms`, `is_trig`, and `max_power` — orchestration |
141+
142+
### Already Using `Postwalk` Effectively
143+
144+
| Function | How | Why it works |
145+
|---|---|---|
146+
| `expand_all` | `Postwalk(expand_exp_power)` after `SymbolicUtils.expand` | Needs to visit all nodes; `expand_exp_power` is a simple leaf check |
147+
| `add_div` | `Postwalk(add_with_div)` | Uses SymbolicUtils-provided `add_with_div` rewriter |
148+
149+
## SymbolicUtils API Quick Reference
150+
151+
For QuestBase developers working with symbolic expressions.
152+
153+
### Expression Type Predicates
154+
- `isadd(x)`, `ismul(x)`, `isdiv(x)`, `ispow(x)` — check structural type
155+
- `isterm(x)` — true for Pow and Term nodes (compound expressions)
156+
- `issym(x)` — true for leaf symbols
157+
- `iscall(x)` — true for ALL compound nodes (more general than `isterm`)
158+
- `is_literal_number(x)` — true for numeric literals
159+
- `is_derivative(x)` — true for derivative expressions
160+
161+
### Expression Accessors
162+
- `arguments(x)` — child expressions (order depends on auto-simplification)
163+
- `sorted_arguments(x)` — children in deterministic order
164+
- `operation(x)` — head operation (`+`, `*`, `sin`, `exp`, etc.)
165+
- `unwrap_const(x)` — extract value from `Const`-wrapped numbers (SymbolicUtils v4)
166+
- `unwrap(x)` / `wrap(x)` — convert between `Num` and `BasicSymbolic`
167+
168+
### Rule System
169+
- `@rule LHS => RHS` — basic pattern rule with `~x` (slot) and `~~xs` (segment)
170+
- `@acrule` — associative-commutative (tries permutations, expensive)
171+
- `@ordered_acrule` — cheaper than `@acrule` (combinations instead of permutations)
172+
- `@capture expr pattern` — one-off pattern match, injects bindings into scope
173+
- `~x::predicate` — slot with predicate filter (e.g., `~x::is_literal_number`)
174+
- `~!x` — default slot (defaults to 0 in `+`, 1 in `*`, 1 in `^` exponent)
175+
176+
### Rewriter Combinators
177+
- `Chain((r1, r2, ...))` — apply in order (use **tuples** for `@generated` unrolling)
178+
- `RestartedChain((r1, r2, ...))` — restart from beginning on any match
179+
- `Fixpoint(rw)` — repeat until no change
180+
- `Postwalk(rw)` / `Prewalk(rw)` — bottom-up / top-down tree traversal
181+
- `PassThrough(rw)` — convert `nothing` return to identity (pass-through)
182+
- `If(cond, rw)` / `IfElse(cond, rw1, rw2)` — conditional dispatch
183+
184+
### Performance Tips
185+
- `COMM_CHECKS_LIMIT` (default 10): commutative matching disabled for 10+ args
186+
- `Postwalk` uses copy-on-write: unchanged subtrees are not rebuilt
187+
- Hash-consing makes `===` checks O(1) for `Fixpoint` termination
188+
- Guard rule groups with `If(predicate, ...)` to skip irrelevant subtrees

src/QuestBase.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ using SymbolicUtils:
1616
issym,
1717
add_with_div,
1818
is_literal_number,
19-
unwrap_const
19+
unwrap_const,
20+
unwrap
2021

2122
using Symbolics:
2223
Symbolics,
2324
Num,
24-
unwrap,
2525
wrap,
2626
get_variables,
2727
Equation,

0 commit comments

Comments
 (0)