-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable dynamic parameters #94
Comments
I just switched from the trapezoid method to BDF2 for numerical integration, which I just realized might help quite a bit with this. It cuts down a lot on the number of places the various functions that come out of analysis need to be evaluated, which is the main problem with dynamic parameters. Only reducing it by a factor of (strictly less than) 2 isn't going to solve it by itself, but maybe it will help... |
I've made some progress in this matter 😃 |
Just pushed my POC here. |
Wow, the demo results are incredible!! Very nice work. The circuit you are simulating is even a reasonably complex circuit! I'm honestly shocked at how well this is working. It's totally reasonable to remove Factor, aside from the bug, it makes sense that the compiler can do CSE better without it. Have you found any circuits that perform worse with this? Is the solve time reasonable? But, really, since this change means that adjusting pots doesn't require solving the circuit again, even if it took multiple seconds, a long solve time would be OK! This is a really great result, awesome work. I feel silly for not figuring this out when I was working on it :) Also, nice work on reviving the old branch, I remember attempting to revive this approach and getting bogged down on something. I think we should do a bit more testing and consider merging your POC, even if simulation performance regresses in some cases. This is a really great improvement, I really miss this approach where pots could be moved during simulation without reinitializing the simulation. I see some seemingly spurious changes in your POC, e.g. swapping resistances and solving order for potentiometers - was this change necessary to get this working? |
@mikeoliphant we had looked at this a while back. @Federerer has made some real progress on getting this working here :) |
Having to re-solve on parameter changes is a major limitation, so this would clearly be a big win. If I recall correctly, I added some code in to delay parameter changing in the VST to keep it from updating the simulation too often. That could probably be removed if this fix is made to allow immediate continuous control. |
This would also make it more practical to consider exposing parameters from the VST to the DAW (they currently are not) to allow for automation. The issue still remains, though, that the parameters change when you load a new circuit - which creates ugly problems with the DAW integration. |
…mall change means that we don't need to solve for differentials before discretization, which may enable dynamic parameters to work! Partially addressing #94
I have huge progress to report on this issue!! It turns out that the row reduction (1) I described in the original report is actually not necessary, with a nearly trivial change that we even already had in the code, just commented out! 5398a63 I need to recognize Andy Simper of Cytomic (https://cytomic.com/, @andy-cytomic) for his contributions here. He reached out to discuss circuit simulation recently, and he pointed out that he doesn't do row reduction here. It turns out that the totally standard nodal analysis of capacitors avoids this, by inserting a new variable into the system to represent the current. LiveSPICE supports this, we just weren't doing it. The code to do it was even there in the code, just commented out! I commented it because it made things just slightly slower. However, it's obviously worth eating that performance hit if it means we can support dynamic parameters properly. I've done some testing with this branch (https://github.com/dsharlet/LiveSPICE/compare/dynamic-parameters), here is where things stand now:
Next steps here:
|
To explain a bit further why that one-line uncommenting matters: Currently, we get differential equations that look like this:
This is not solvable, it has more equations than unknowns (the differentials). Row reduction fixes this. However, row reduction has a horrible side effect of blowing up the expressions for the rest of the nodes in the circuit. When those nodes are mostly constants, it's a minor problem. However, if those expressions have symbols/variables like those from potentiometers, this is terrible. The expressions get so huge that the row reduction basically takes forever, I don't think I ever even bothered to let it finish on even modest circuits because it was impractically slow. Now, if you apply 5398a63, the differential equations look like this:
No row reduction needed! In fact, I haven't even removed the row reduction step from the code, it's just automatically nearly a no-op now. We still need it basically just to sort the differentials to the top-left of the matrix, but that could easily be done more directly if needed. Now we can let the expressions with dynamic parameter symbols/variables just fall through to the non-linear solver, which can handle them fine. It does slow down the simulation (linear variables are much faster to solve than non-linear during simulation), but I think it's worth the tradeoff, especially because I suspect more variables than necessary end up in the non-linear solver now. |
Another update: almost all of the numerical issues were just silly bugs about potentiometers getting too close to extreme values. Some code cleanups fix those. The only remaining stability issue is one circuit that takes ages to solve (at least 10x longer than any other circuit), and when it finally does solve and run, it diverges. Some circuits are actually faster on this branch, like the Phase 90, which now easily runs in real time. This used to be one of the toughest circuits! |
Very cool! |
Nice, I have to check it on my branch ;) |
Here is the branch I'm working on: https://github.com/dsharlet/LiveSPICE/compare/dynamic-parameters I think it's almost ready to merge. It has just one circuit in all of the tests that doesn't work, and I just want to try strengthening the computer algebra to simplify some of the new patterns resulting from this change, to hopefully recover some performance loss. But even the performance loss as it exists now is OK I think. I'm basically just hoping that cleaning up the math will somehow magically fix the broken circuit, or at least make it easier to debug. |
Once you merge, I can update the VST to skip the re-solve on parameter change. |
Currently, when you change a potentiometer or other circuit control, the simulation has to be resolved, which takes a while, and loses the state of the circuit.
A while back, this worked differently: the potentiometers and other parameters were actual parameters of the simulation function, and could be adjusted smoothly and dynamically. This was a much better user experience and would make circuits like Wah pedals a lot more useful and interesting.
However, this approach really struggled, because the non-constant expressions would get gigantic during row reduction. I ended up dropping this approach in favor of the current simulation rebuilding to avoid this: b9f03ca
There are two row reductions that currently happen:
(Background: http://dsharlet.com/2014/03/28/how-livespice-works-numerically-solving-circuit-equations/)
I think (1) is strictly necessary. However, I think (2) is (partially) not necessary, and even a bad idea sometimes. I think maybe we only need to partially row reduce to get the number of variables down to the number of non-linear equations. Currently, the whole system is row reduced such that after solving the non-linear system, there is only back substitution to do. But this might not be necessary, solving a linear system dynamically isn't bad (and could benefit a lot from SIMD), and might even have other benefits (we could choose pivots, which isn't possible symbolically).
The problem here is I'm not actually sure how to do this. It's not possible to simply row reduce anywhere we'd like, we need a pivot in every column we want to zero out, and you can't get that pivot by zeroing out every column before that.
We could just take the linear variables as the values from the previous timestep, solve the non-linear equations, and then get the new linear equations to solve. That would basically be a block-wise (with 2 blocks) Gauss-Seidel type thing. It seems a lot of hobbyist circuit simulators do something like this (usually without realizing it). I've generally tried to avoid Gauss-Seidel as it just seems sketchy, and should require more iterations (linear vs. quadratic convergence). But maybe this doesn't really matter in practice.
I think "real" large SPICE simulators use Gauss-Seidel a lot more, which would make sense (large sparse systems are going to be bad with Newton's method). But they're also a lot slower...
It's also possible that (1) by itself is enough to make dynamic controls infeasible.
The text was updated successfully, but these errors were encountered: