mathnet-symbolics
mathnet-symbolics copied to clipboard
No effective way to generate large sums
Hi!
currently I'm trying to generate a large symbolic expression (over 1 000 000 terms). Unfortunately, performance was not very high so I wrote a benchmark to demonstrate the problem.
The idea of a benchmark is to generate a list of n nonlinear terms (so they wouldn't collapse to one term) and then calculate their sum. I used sin(ix)*cos(iy), i=1..n for nonlinear terms and wrote minimal test code:
open MathNet.Symbolics
open Operators
[<EntryPoint>]
let main argv =
let x = symbol "x"
let y = symbol "y"
let terms n= seq {for i in 1..n -> sin(i*x)*cos(i*y)}
let sw = new System.Diagnostics.Stopwatch()
sw.Start()
let res = terms 10_000 |> Seq.toList |> sum
sw.Stop()
printfn "Time %f" sw.Elapsed.TotalMilliseconds
0
The test evaluated for 53.5 seconds. For a more detailed analysis I excluded list generation from time measurement:
open MathNet.Symbolics
open Operators
[<EntryPoint>]
let main argv =
let x = symbol "x"
let y = symbol "y"
let terms n= seq {for i in 1..n -> sin(i*x)*cos(i*y)}
let sw = new System.Diagnostics.Stopwatch()
let evaledTerms = terms 10_000 |> Seq.toList
sw.Start()
let res = evaledTerms |> sum
sw.Stop()
printfn "Time %f" sw.Elapsed.TotalMilliseconds
0
Current time is 53.3 seconds. So the problem is in the sum method.
Expressions.fs shows that sum is realized using reduce:
let sum (xs:Expression list) : Expression = if List.isEmpty xs then zero else List.reduce add xs
So to create a sum of n elements it will call add function n times which is not an effective way to generate large sums.
Is there any "unobvious" method in Symbolics to generate this sum from list without using reduce? Using debugger I can see that the final expression contains a list of terms. Can I generate sum by directly passing a list of terms without creating n temporary accumulators?
Create your own sum function with:
Definition.funDict.TryRemove "mySum"
Definition.funDict.TryAdd ("mySum", (DTProc ([
[symX], (DBFun ((fun parentScopeIdOpt procEnv symbolValues exprs ->
let stx = procEnv.stx
let _, (nx) = stx.Value.TryGetValue "x"
printfn "%A" nx
let (NestedExpr X) = nx
let v =
X
|> List.map (fun v ->
match v with
| (Number br) -> br
| (Approximation (Real f)) ->
BigRational.FromDecimal (decimal f)
)
|> List.sum |> BR
{
procEnv
with
prevOutput = Some v
}
), OutFP))
], 0, None)))
(SymbolicExpression.Parse "mySum(expr(1,2,3,4/5,6/7,8.3))").Evaluate(dict [])
And the result would be:
NestedExpr
[Number 1N; Number 2N; Number 3N; Number 4/5N; Number 6/7N;
Approximation (Real 8.3)]
val it: FloatingPoint = BR 1117/70N
https://github.com/ingted/symbolic.net/blob/main/SymbolicNet6/scripts/test.20250618.fsx