pub struct NewtonRaphsonSolver { /* private fields */ }Expand description
Newton-Raphson solver for systems of nonlinear equations
This solver can handle:
- Square systems (equations == variables)
- Under-constrained systems (equations < variables) - uses least squares
- Over-constrained systems (equations > variables) - uses least squares
The solver uses adaptive damping and regularization for numerical stability.
Implementations§
Source§impl NewtonRaphsonSolver
impl NewtonRaphsonSolver
Sourcepub fn new(compiled: CompiledSystem) -> Self
pub fn new(compiled: CompiledSystem) -> Self
Create a new Newton-Raphson solver with adaptive parameters
Parameters are automatically adjusted based on system type:
- Over-constrained systems get more iterations, relaxed tolerance, conservative damping
- Normal systems use standard parameters for fast convergence
§Arguments
compiled- Compiled system of equations (each should evaluate to 0 at solution)
Examples found in repository?
37fn main() {
38 // Two circles with radius 3 centered at (0, 0) and (4, 0).
39 let x = Exp::var("x");
40 let y = Exp::var("y");
41
42 let eq1 = circle_eq(&x, &y, 0.0, 0.0, 3.0);
43 let eq2 = circle_eq(&x, &y, 4.0, 0.0, 3.0);
44
45 let compiled = Compiler::compile(&[eq1, eq2]).expect("compile failed");
46 let solver = NewtonRaphsonSolver::new(compiled);
47
48 let mut initial = HashMap::new();
49 initial.insert("x".to_string(), 2.0);
50 initial.insert("y".to_string(), 1.0);
51
52 let solution = solver.solve(initial).expect("solve failed");
53 let x_sol = solution.values.get("x").copied().unwrap();
54 let y_sol = solution.values.get("y").copied().unwrap();
55
56 println!("intersection: x={:.6}, y={:.6}", x_sol, y_sol);
57}More examples
28fn main() {
29 // Simple resistive divider with explicit currents.
30 let vout = Exp::var("vout");
31 let i1 = Exp::var("i1");
32 let i2 = Exp::var("i2");
33
34 let vin = 12.0;
35 let r1 = 1_000.0;
36 let r2 = 2_000.0;
37
38 // (Vin - Vout) / R1 = I1
39 let eq1 = Exp::sub(
40 Exp::div(Exp::sub(Exp::val(vin), vout.clone()), Exp::val(r1)),
41 i1.clone(),
42 );
43 // Vout / R2 = I2
44 let eq2 = Exp::sub(Exp::div(vout.clone(), Exp::val(r2)), i2.clone());
45 // KCL: I1 = I2
46 let eq3 = Exp::sub(i1.clone(), i2.clone());
47
48 let compiled = Compiler::compile(&[eq1, eq2, eq3]).expect("compile failed");
49 let solver = NewtonRaphsonSolver::new(compiled);
50
51 let mut initial = HashMap::new();
52 initial.insert("vout".to_string(), 8.0);
53 initial.insert("i1".to_string(), 0.004);
54 initial.insert("i2".to_string(), 0.004);
55
56 let solution = solver.solve(initial).expect("solve failed");
57 let vout_sol = solution.values.get("vout").copied().unwrap();
58 let i_sol = solution.values.get("i1").copied().unwrap();
59
60 println!("vout={:.6} V, current={:.6} A", vout_sol, i_sol);
61}28fn main() {
29 // Series resistor + diode with Shockley equation.
30 let i = Exp::var("i");
31 let vd = Exp::var("vd");
32
33 let vs = 5.0;
34 let r = 1_000.0;
35 let isat = 1e-12;
36 let n = 1.0;
37 let vt = 0.02585;
38
39 // KVL: Vs - I*R - Vd = 0
40 let kvl = Exp::sub(Exp::sub(Exp::val(vs), Exp::mul(i.clone(), Exp::val(r))), vd.clone());
41
42 // I - Is * (exp(Vd / (n*Vt)) - 1) = 0
43 let exp_arg = Exp::div(vd.clone(), Exp::val(n * vt));
44 let diode_i = Exp::mul(
45 Exp::val(isat),
46 Exp::sub(Exp::exp(exp_arg), Exp::val(1.0)),
47 );
48 let diode_eq = Exp::sub(i.clone(), diode_i);
49
50 let compiled = Compiler::compile(&[kvl, diode_eq]).expect("compile failed");
51 let solver = NewtonRaphsonSolver::new(compiled);
52
53 let mut initial = HashMap::new();
54 initial.insert("i".to_string(), 0.005);
55 initial.insert("vd".to_string(), 0.7);
56
57 let solution = solver.solve(initial).expect("solve failed");
58 let i_sol = solution.values.get("i").copied().unwrap();
59 let vd_sol = solution.values.get("vd").copied().unwrap();
60
61 println!("diode: i={:.6} A, vd={:.6} V", i_sol, vd_sol);
62}Sourcepub fn new_with_variables(
compiled: CompiledSystem,
variables: &[&str],
) -> Result<Self, SolverError>
pub fn new_with_variables( compiled: CompiledSystem, variables: &[&str], ) -> Result<Self, SolverError>
Create a solver while specifying which variables to solve for.
Variables not listed here are treated as fixed parameters and must be provided in the initial guess.
Examples found in repository?
28fn main() {
29 // Find point B given fixed A, fixed length, and horizontal constraint.
30 let ax = Exp::var("ax");
31 let ay = Exp::var("ay");
32 let bx = Exp::var("bx");
33 let by = Exp::var("by");
34
35 let length = 5.0;
36 let y_target = 2.0;
37
38 let dx = Exp::sub(bx.clone(), ax.clone());
39 let dy = Exp::sub(by.clone(), ay.clone());
40 let length_eq = Exp::sub(
41 Exp::add(Exp::power(dx, 2.0), Exp::power(dy, 2.0)),
42 Exp::val(length * length),
43 );
44 let horizontal_eq = Exp::sub(by.clone(), Exp::val(y_target));
45
46 let compiled = Compiler::compile(&[length_eq, horizontal_eq]).expect("compile failed");
47 let solver = NewtonRaphsonSolver::new_with_variables(compiled, &["bx", "by"])
48 .expect("solver init failed");
49
50 let mut initial = HashMap::new();
51 initial.insert("ax".to_string(), 1.0);
52 initial.insert("ay".to_string(), 2.0);
53 initial.insert("bx".to_string(), 6.0);
54 initial.insert("by".to_string(), 2.0);
55
56 let solution = solver.solve(initial).expect("solve failed");
57 let bx_sol = solution.values.get("bx").copied().unwrap();
58 let by_sol = solution.values.get("by").copied().unwrap();
59
60 println!("B = ({:.6}, {:.6})", bx_sol, by_sol);
61}Sourcepub fn try_with_equation_traces(
self,
traces: Vec<Option<EquationTrace>>,
) -> Result<Self, SolverError>
pub fn try_with_equation_traces( self, traces: Vec<Option<EquationTrace>>, ) -> Result<Self, SolverError>
Attach metadata describing the origin of each equation in the system
Sourcepub fn with_equation_traces(self, traces: Vec<Option<EquationTrace>>) -> Self
pub fn with_equation_traces(self, traces: Vec<Option<EquationTrace>>) -> Self
Attach metadata describing the origin of each equation in the system
Sourcepub fn trace_for_equation(
&self,
equation_index: usize,
) -> Option<&EquationTrace>
pub fn trace_for_equation( &self, equation_index: usize, ) -> Option<&EquationTrace>
Fetch trace metadata for a specific equation, if available
Sourcepub fn with_max_iterations(self, max_iterations: usize) -> Self
pub fn with_max_iterations(self, max_iterations: usize) -> Self
Override the maximum number of iterations
Sourcepub fn with_tolerance(self, tolerance: f64) -> Self
pub fn with_tolerance(self, tolerance: f64) -> Self
Override the convergence tolerance
Sourcepub fn with_damping(self, damping_factor: f64) -> Self
pub fn with_damping(self, damping_factor: f64) -> Self
Override the damping factor (clamped to [0.1, 1.0] for stability)
Sourcepub fn with_regularization(self, regularization: f64) -> Self
pub fn with_regularization(self, regularization: f64) -> Self
Override the base regularization parameter used when the system is ill-conditioned
Sourcepub fn solve(
&self,
initial_guess: HashMap<String, f64>,
) -> Result<Solution, SolverError>
pub fn solve( &self, initial_guess: HashMap<String, f64>, ) -> Result<Solution, SolverError>
Solve the system using standard Newton-Raphson method
Examples found in repository?
37fn main() {
38 // Two circles with radius 3 centered at (0, 0) and (4, 0).
39 let x = Exp::var("x");
40 let y = Exp::var("y");
41
42 let eq1 = circle_eq(&x, &y, 0.0, 0.0, 3.0);
43 let eq2 = circle_eq(&x, &y, 4.0, 0.0, 3.0);
44
45 let compiled = Compiler::compile(&[eq1, eq2]).expect("compile failed");
46 let solver = NewtonRaphsonSolver::new(compiled);
47
48 let mut initial = HashMap::new();
49 initial.insert("x".to_string(), 2.0);
50 initial.insert("y".to_string(), 1.0);
51
52 let solution = solver.solve(initial).expect("solve failed");
53 let x_sol = solution.values.get("x").copied().unwrap();
54 let y_sol = solution.values.get("y").copied().unwrap();
55
56 println!("intersection: x={:.6}, y={:.6}", x_sol, y_sol);
57}More examples
28fn main() {
29 // Simple resistive divider with explicit currents.
30 let vout = Exp::var("vout");
31 let i1 = Exp::var("i1");
32 let i2 = Exp::var("i2");
33
34 let vin = 12.0;
35 let r1 = 1_000.0;
36 let r2 = 2_000.0;
37
38 // (Vin - Vout) / R1 = I1
39 let eq1 = Exp::sub(
40 Exp::div(Exp::sub(Exp::val(vin), vout.clone()), Exp::val(r1)),
41 i1.clone(),
42 );
43 // Vout / R2 = I2
44 let eq2 = Exp::sub(Exp::div(vout.clone(), Exp::val(r2)), i2.clone());
45 // KCL: I1 = I2
46 let eq3 = Exp::sub(i1.clone(), i2.clone());
47
48 let compiled = Compiler::compile(&[eq1, eq2, eq3]).expect("compile failed");
49 let solver = NewtonRaphsonSolver::new(compiled);
50
51 let mut initial = HashMap::new();
52 initial.insert("vout".to_string(), 8.0);
53 initial.insert("i1".to_string(), 0.004);
54 initial.insert("i2".to_string(), 0.004);
55
56 let solution = solver.solve(initial).expect("solve failed");
57 let vout_sol = solution.values.get("vout").copied().unwrap();
58 let i_sol = solution.values.get("i1").copied().unwrap();
59
60 println!("vout={:.6} V, current={:.6} A", vout_sol, i_sol);
61}28fn main() {
29 // Series resistor + diode with Shockley equation.
30 let i = Exp::var("i");
31 let vd = Exp::var("vd");
32
33 let vs = 5.0;
34 let r = 1_000.0;
35 let isat = 1e-12;
36 let n = 1.0;
37 let vt = 0.02585;
38
39 // KVL: Vs - I*R - Vd = 0
40 let kvl = Exp::sub(Exp::sub(Exp::val(vs), Exp::mul(i.clone(), Exp::val(r))), vd.clone());
41
42 // I - Is * (exp(Vd / (n*Vt)) - 1) = 0
43 let exp_arg = Exp::div(vd.clone(), Exp::val(n * vt));
44 let diode_i = Exp::mul(
45 Exp::val(isat),
46 Exp::sub(Exp::exp(exp_arg), Exp::val(1.0)),
47 );
48 let diode_eq = Exp::sub(i.clone(), diode_i);
49
50 let compiled = Compiler::compile(&[kvl, diode_eq]).expect("compile failed");
51 let solver = NewtonRaphsonSolver::new(compiled);
52
53 let mut initial = HashMap::new();
54 initial.insert("i".to_string(), 0.005);
55 initial.insert("vd".to_string(), 0.7);
56
57 let solution = solver.solve(initial).expect("solve failed");
58 let i_sol = solution.values.get("i").copied().unwrap();
59 let vd_sol = solution.values.get("vd").copied().unwrap();
60
61 println!("diode: i={:.6} A, vd={:.6} V", i_sol, vd_sol);
62}28fn main() {
29 // Find point B given fixed A, fixed length, and horizontal constraint.
30 let ax = Exp::var("ax");
31 let ay = Exp::var("ay");
32 let bx = Exp::var("bx");
33 let by = Exp::var("by");
34
35 let length = 5.0;
36 let y_target = 2.0;
37
38 let dx = Exp::sub(bx.clone(), ax.clone());
39 let dy = Exp::sub(by.clone(), ay.clone());
40 let length_eq = Exp::sub(
41 Exp::add(Exp::power(dx, 2.0), Exp::power(dy, 2.0)),
42 Exp::val(length * length),
43 );
44 let horizontal_eq = Exp::sub(by.clone(), Exp::val(y_target));
45
46 let compiled = Compiler::compile(&[length_eq, horizontal_eq]).expect("compile failed");
47 let solver = NewtonRaphsonSolver::new_with_variables(compiled, &["bx", "by"])
48 .expect("solver init failed");
49
50 let mut initial = HashMap::new();
51 initial.insert("ax".to_string(), 1.0);
52 initial.insert("ay".to_string(), 2.0);
53 initial.insert("bx".to_string(), 6.0);
54 initial.insert("by".to_string(), 2.0);
55
56 let solution = solver.solve(initial).expect("solve failed");
57 let bx_sol = solution.values.get("bx").copied().unwrap();
58 let by_sol = solution.values.get("by").copied().unwrap();
59
60 println!("B = ({:.6}, {:.6})", bx_sol, by_sol);
61}Sourcepub fn solve_with_line_search(
&self,
initial_guess: HashMap<String, f64>,
) -> Result<Solution, SolverError>
pub fn solve_with_line_search( &self, initial_guess: HashMap<String, f64>, ) -> Result<Solution, SolverError>
Solve the system using Newton-Raphson with line search
Line search helps improve convergence robustness by automatically finding good step sizes, especially useful for difficult systems.