Hi Andrew,

Il 04/12/23 02:02, Andrew Simper ha scritto:
I chose to base my symbolic system on how I can see Mathematica represents things: an n-ary expression tree, which is working out well for me. I'm using a sorted n-ary tree structure where each node has a "head" saying what the node is and a set of links to other nodes. I use these fundamental mathematical operations: Add, Times, Power, but derive subtraction and division from them: a - b  = add(a, times(-1 ,b)), a / b = times(a, power(b, -1)), which really helps when doing simplifications, and is how Mathematica does things. Another powerful concept in Mathematica are "rules" for pattern matching and expressions manipulation, for example a_*b_ +  a_*c_ -> a_*(b_ + c_), where the a_ means "any expressions" and something like a_Integer, or a_Symbol just means a single integer or variable respectively. Finder matching sub expressions, including negative ones, is still something I want to optimise further.

I've got something similar: n-ary trees where each node is an operation or a constant/variable, the basic operations are sum (+ and -), multiplication (* and /), power, and "function" which represents any other kind of operation specialized via JavaScript's prototype inheritance. Sums and multiplications can have an arbitrary number of operands. There's a bunch of very specific details on why I chose this specific representation but, TBH, I don't remember everything (I developed this stuff around end of 2020 and didn't touch it since then) - what I do remember is that this allows to efficiently solve linear equations and to check for equality of expressions at least.

I remember I also initially tried implementing a pattern matching mechanism to specify substitutions, but IIRC I ditched the idea as it would have been really complex and it would be hard anyway to understand which substitutions would need to applied when in the first place. And indeed...

I think it's an interesting optimization problem to solve these systems efficiently, that from my point of view looks NP-Hard, much like the traveling salesman problem. The problem statement goes something like: "Solve A.x = z for x (x and z being vectors of length n, and A being an n by n matrix) in the least "cost" in fundamental operations =, +, -, *, / where the cost of operations are zero for =, one for + - *, and some higher number for eg 5 for /. The resultant solution will usually can be written as a series of assignments of temporary variables to (possibly common) sub-expressions of the final solution, ending with assignments to the n elements of x being the solution to the system of linear equations.

... that is what happens. :-)

In practice, I noticed that for the application at hand (solving the linear part of circuit models):

1. matrix triangulation via Gauss elimination seems to reduce the number of operations in total compared to e.g. substitution (in any case, you get one division per row, which is always the worst offender and in most cases unavoidable);

2. choosing the order of rows is extremely important w.r.t. the number of operations you actually end up performing, especially in case you have several parameters - that is why the software I built let's the user play with such order freely.

Nailing these two aspects leads to good enough results IMO, without getting into automatic expression optimization (you can always do that by hand afterwards if really needed).

I just coupled all of this with some rudimentary a SPICE netlist parser with the possibility to have expressions as component values (including unknown parameters), some code to automatically derive circuit equations and "categorize" them in different sets, a rudimentary HTML GUI, a flexible discretization approach [1], and Ciaramella (https://ciaramella.dev/) code generation from the triangulated matrix, and voila! :-)

I'm surprised, given the usefulness, that there aren't more papers published in this area, perhaps I just don't know what to look for!

I also couldn't find much back then. I know there were a couple of books on the matter, one of which too basic to be useful, so I just did it from scratch my way. Maybe one could go and study Maxima's source code or something.

Best,

Stefano

[1] F. Germain, K. Werner, "Design Principles for Lumped Model Discretisation Using Mobius Transforms", DAFx-15.

Reply via email to