geop_core_math/solvers/system/mod.rs
1//! Constraint systems: values a solve moves, in blocks ([`Variable`]s — a
2//! number, a pose), and terms on them ([`Equations`]) — constraints that
3//! hold where their residuals vanish (or, for a limit, stay at most zero),
4//! and preferences a solve pulls towards among where the constraints hold.
5//! Written once, for every system of the kernel: a sketch's numbers
6//! (`geop_core_sketch`) and a program's state, where its parts are
7//! (`geop_core_program::solve`), are each only an [`Equations`].
8//!
9//! On a [`System`] — the equations, how each block may move, the system's
10//! size — the algorithms are generic:
11//!
12//! - **Groups.** Blocks no term ties together are solved, enclosed and
13//! measured apart ([`System::structure`]): a plate with hundreds of
14//! screws is hundreds of systems of one screw. The answer is the same,
15//! since what a solve minimizes is a sum over the groups; a group whose
16//! constraints hold and that nothing pulls is not solved at all. Within a
17//! group, a block whose constraints only hang it off others — a screw on
18//! a bracket, the bracket on a link — is a pendant: solved and enclosed
19//! after them, alone ([`structure`]), so a chain of links carrying
20//! hundreds of parts is a small system and hundreds of tiny ones, not one
21//! dense system of every part. The groups of a layer are worked on in
22//! lockstep ([`lockstep`]): one evaluation answers every group waiting,
23//! so the evaluations a solve takes do not grow with the number of
24//! groups.
25//! - **Solve** ([`System::solve`]) by the driver every solve shares
26//! ([`driver`]): the constraints held exactly, the
27//! preferences and staying put ([`Variable::stay`]) only choosing among
28//! where they hold. Each block moves by steps along it
29//! ([`Variable::retract`]). A limit ([`Bound::AtMost`]) is kept by an
30//! active set: solved without the limits first, every one the solve ends
31//! beyond is held at its bound, and the group is solved again from where
32//! it started.
33//! - **What holds** ([`System::held`]), to [`RELATIVE_TOLERANCE`]
34//! of the size.
35//! - **Enclosure** ([`System::enclose`]): a rigorous box around a true
36//! solution near where a solve ended — Krawczyk's test over the
37//! independent rows, chosen over a box — or why there is none to be had:
38//! a singular configuration, or constraints that conflict, by name.
39//! - **Freedom** ([`System::freedom`]): how many independent ways each
40//! block can still move, decided over the box the enclosure proved. A
41//! limit leaves the motion away from it free.
42//! - **Conflicts** ([`System::conflicting`]): a smallest set of constraints
43//! that cannot all hold together.
44//!
45//! A block is enclosed in its own step coordinates around where it is: a
46//! value `x = retract(x̃, δ)` for a box of steps `δ`. The Jacobian of a
47//! term by `δ` is its Jacobian by the steps at `x` — what [`Equations`]
48//! gives — times the differential of the retraction
49//! ([`Variable::differential`]): the identity for a number, the right
50//! Jacobian of the exponential map for a pose.
51
52use crate::{
53 geop_error::{GeopError, GeopResult},
54 scalars::Scalar,
55 solvers::least_squares::Rows,
56};
57
58mod driver;
59mod enclose;
60mod lockstep;
61mod structure;
62
63pub use driver::{DAMPING, Mobility, Phase, RELATIVE_TOLERANCE, tolerance};
64use driver::{Values, largest};
65
66pub use enclose::{EncloseError, Enclosure};
67use lockstep::{Batch, Made};
68pub use structure::Structure;
69
70/// A value a solve moves: a block of variables, stepped along its own
71/// tangent space.
72pub trait Variable: Clone + std::fmt::Debug {
73 /// How many numbers a step of it takes. None, for a value no solve
74 /// moves.
75 fn dimension(&self) -> usize;
76
77 /// Where `step` — [`Variable::dimension`] numbers — takes it: the value
78 /// a solver tries next, a free choice, so sharp.
79 fn retract(&self, step: &[Scalar]) -> GeopResult<Self>;
80
81 /// Every value a step in the box `step` takes it to: an enclosure,
82 /// nothing sharpened — of the value it stands for, which a sharp value
83 /// may only approximate (a rotation's unit quaternion).
84 fn retract_over(&self, step: &[Scalar]) -> GeopResult<Self>;
85
86 /// The differential of the retraction over the box `step`: the matrix
87 /// `D`, one row per number of a step, such that for every `δ` in the box
88 /// a step `dδ` further, `retract(δ + dδ)`, is to first order the step
89 /// `D dδ` from `retract(δ)` — an enclosure for every `δ` in the box.
90 /// `None` for the identity: a step that adds to the value.
91 fn differential(&self, step: &[Scalar]) -> GeopResult<Option<Vec<Vec<Scalar>>>>;
92
93 /// What keeps it near `at` where nothing else decides where it goes:
94 /// residuals that vanish there, each a length — a turn measured where
95 /// it moves a point `scale` from its axis — and their derivatives by
96 /// its step.
97 fn stay(&self, at: &Self, scale: Scalar) -> GeopResult<Rows>;
98
99 /// Whether it is no different from `other`: a step that changed nothing.
100 fn same(&self, other: &Self) -> bool;
101
102 /// Where it goes carried along by `leader` moving from `from` to `to`,
103 /// as if fastened to it — a seed for a solve of what hangs off the
104 /// leader (see [`Structure`]), a free choice, so sharp. `None` for a
105 /// value nothing carries: a number.
106 fn carried(&self, from: &Self, to: &Self) -> Option<Self> {
107 let _ = (from, to);
108 None
109 }
110}
111
112/// A number steps by what is added to it.
113impl Variable for Scalar {
114 fn dimension(&self) -> usize {
115 1
116 }
117
118 fn retract(&self, step: &[Scalar]) -> GeopResult<Scalar> {
119 Ok(self.add(*one(step)?).sharpen())
120 }
121
122 fn retract_over(&self, step: &[Scalar]) -> GeopResult<Scalar> {
123 let d = *one(step)?;
124 Ok(match d.is_zero() && d.is_sharp() {
125 true => *self,
126 false => self.add(d),
127 })
128 }
129
130 fn differential(&self, step: &[Scalar]) -> GeopResult<Option<Vec<Vec<Scalar>>>> {
131 one(step)?;
132 Ok(None)
133 }
134
135 fn stay(&self, at: &Scalar, _: Scalar) -> GeopResult<Rows> {
136 let mut rows = Rows::default();
137 rows.push(self.sub(*at), vec![Scalar::ONE]);
138 Ok(rows)
139 }
140
141 fn same(&self, other: &Scalar) -> bool {
142 self.could_be_equal(*other)
143 }
144}
145
146/// The one number of a step of a number.
147fn one(step: &[Scalar]) -> GeopResult<&Scalar> {
148 match step {
149 [d] => Ok(d),
150 _ => Err(GeopError::new(format!(
151 "a step of a number is one number, not {}",
152 step.len()
153 ))),
154 }
155}
156
157/// What a constraint asks of its residuals.
158#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
159pub enum Bound {
160 /// That they vanish: it holds where they are zero.
161 #[default]
162 Zero,
163 /// That none is above zero: a limit, which holds anywhere short of it.
164 AtMost,
165}
166
167impl Bound {
168 /// Whether the residual `r` holds — within `tolerance` of zero, or,
169 /// for a limit, not definitely above `tolerance`.
170 pub fn holds(self, r: Scalar, tolerance: Scalar) -> bool {
171 r.is_finite()
172 && match self {
173 Bound::Zero => !r.abs().definitely_greater(tolerance),
174 Bound::AtMost => !r.definitely_greater(tolerance),
175 }
176 }
177}
178
179/// A term of a system: a constraint or a preference, by index.
180#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
181pub enum Term {
182 Constraint(usize),
183 Preference(usize),
184}
185
186/// A term's residuals where the blocks are, each a length, and their
187/// derivatives: per block it reads, by index, one row per residual — its
188/// derivative by that block's step from where it is.
189#[derive(Clone, Debug, Default, PartialEq)]
190pub struct Linearized {
191 pub values: Vec<Scalar>,
192 pub jacobian: Vec<(usize, Vec<Vec<Scalar>>)>,
193}
194
195/// The terms of a system: what an adapter — a sketch, a program — says of
196/// its constraints and preferences.
197pub trait Equations {
198 type Value: Variable;
199
200 /// How many constraints there are, and how many preferences.
201 fn count(&self) -> (usize, usize);
202
203 /// What the constraint `constraint` asks of its residuals.
204 fn bound(&self, constraint: usize) -> Bound;
205
206 /// The terms `which`, with every block at `values`: each its residuals
207 /// and their derivatives, or `None` where it cannot be made there (a
208 /// residual undecidable — a direction of zero length — or the term not
209 /// made at all).
210 fn evaluate(&self, values: &[Self::Value], which: &[Term]) -> Vec<Option<Linearized>>;
211}
212
213/// Blocks a solve works on together, and the terms on them (see
214/// [`Structure`]): a core no term ties to another, or a pendant hanging
215/// off blocks of earlier layers.
216#[derive(Clone, Debug, Default, PartialEq, Eq)]
217pub struct Group {
218 /// Its blocks, in order: what its variables are, one after another.
219 pub blocks: Vec<usize>,
220 pub constraints: Vec<usize>,
221 pub preferences: Vec<usize>,
222 /// The blocks outside it its terms read: what it hangs off. None for a
223 /// core.
224 pub on: Vec<usize>,
225 /// Its layer: none for a core, one past every group it hangs off for a
226 /// pendant.
227 pub layer: usize,
228 /// The component it is part of, by index (see
229 /// [`Structure::components`]).
230 pub component: usize,
231 /// For a pendant, how many ways its terms leave it to move, what it
232 /// hangs off held.
233 pub own: usize,
234}
235
236/// Which constraints hold, and the largest residual: an upper bound,
237/// infinite where one cannot be made.
238#[derive(Clone, Debug, PartialEq)]
239pub struct Held {
240 /// Every constraint holds, to [`RELATIVE_TOLERANCE`] of
241 /// the size.
242 pub converged: bool,
243 pub max_residual: Scalar,
244 /// The constraints that do not hold, by index.
245 pub failed: Vec<usize>,
246}
247
248/// Where a solve put the blocks, and how it went.
249#[derive(Debug)]
250pub struct Solution<V: Variable> {
251 pub values: Vec<V>,
252 /// The groups moved: those a preference pulled, whose constraints did
253 /// not hold, or that hang off a group that moved.
254 pub solved: Vec<Group>,
255 /// The limits the solve held at their bounds, by index.
256 pub at_limit: Vec<usize>,
257 /// The most steps any group took, and per phase the worst of the
258 /// groups: why it stopped, after how many steps, the largest residual
259 /// left.
260 pub iterations: usize,
261 pub phases: Vec<Phase>,
262 /// How many times the solve made the terms — every group's at once:
263 /// the work it took, its enclosure's apart.
264 pub evaluations: usize,
265 /// An enclosure of a true solution near where the solved groups ended
266 /// (see [`System::enclose`]) — of those only: the others were not
267 /// moved, and enclosing them is the caller's to ask
268 /// ([`System::enclose_rest`]). Of none, for a solve not proven
269 /// ([`System::solve_unproven`]).
270 pub enclosure: Enclosure<V>,
271 /// The groups it was solved in: what a solve of the same system a hair
272 /// away can take as it is ([`System::solve_unproven_in`]).
273 pub structure: Structure,
274}
275
276/// How free the blocks are (see [`System::freedom`]).
277#[derive(Clone, Debug, PartialEq)]
278pub struct Freedom {
279 /// Per block, how many independent ways it can still move, the others
280 /// moving with it as the constraints need.
281 pub blocks: Vec<usize>,
282 /// How many independent ways they can move together.
283 pub total: usize,
284}
285
286/// A system: its equations, how each block may move — every one a solve
287/// may move, [`Mobility::Held`] or [`Mobility::Follows`] — and its size.
288pub struct System<'e, E: Equations> {
289 pub equations: &'e E,
290 pub mobility: Vec<Mobility>,
291 /// What a constraint holds to [`RELATIVE_TOLERANCE`] of,
292 /// how far one step may go, and what a turn is measured against when a
293 /// value stays where it was.
294 pub scale: Scalar,
295}
296
297/// Every term of a system with `count` constraints and preferences.
298fn every_term((constraints, preferences): (usize, usize)) -> Vec<Term> {
299 (0..constraints)
300 .map(Term::Constraint)
301 .chain((0..preferences).map(Term::Preference))
302 .collect()
303}
304
305impl<E: Equations> System<'_, E> {
306 /// Which constraints hold at `at`.
307 pub fn held(&self, at: &[E::Value]) -> GeopResult<Held> {
308 let (count, _) = self.equations.count();
309 let constraints: Vec<Term> = (0..count).map(Term::Constraint).collect();
310 let tolerance = tolerance(self.scale)?;
311 let mut failed = Vec::new();
312 let mut values = Vec::new();
313 for (c, made) in self.equations.evaluate(at, &constraints).iter().enumerate() {
314 let bound = self.equations.bound(c);
315 let holds = match made {
316 Some(made) => made.values.iter().fold(true, |holds, &r| {
317 values.push(r);
318 holds && bound.holds(r, tolerance)
319 }),
320 None => {
321 values.push(Scalar::INFINITY);
322 false
323 }
324 };
325 if !holds {
326 failed.push(c);
327 }
328 }
329 Ok(Held {
330 converged: failed.is_empty(),
331 max_residual: largest(values),
332 failed,
333 })
334 }
335
336 /// Where the blocks have to be, from `start`, for every constraint to
337 /// hold, as near where the preferences pull them and, among those, as
338 /// near `start` as the constraints allow — and an enclosure of a true
339 /// solution near there.
340 ///
341 /// Layer by layer (see [`Structure`]): the cores first, then each
342 /// pendant, alone, from where what it hangs off carried it
343 /// ([`Variable::carried`]) — solved only where its constraints do not
344 /// hold there, or where its own freedom has to stay as near where it
345 /// was as they let it. A component a pendant of which cannot follow
346 /// after all is solved again as one.
347 ///
348 /// The blocks are moved even where the constraints cannot all hold, to
349 /// where they come closest; [`System::held`] says which do not.
350 pub fn solve(&self, start: &[E::Value]) -> GeopResult<Solution<E::Value>> {
351 self.proven(self.solve_unproven(start)?)
352 }
353
354 /// `solution`, a solve of it, with the enclosure of every group it
355 /// solved (see [`System::solve`]).
356 pub fn proven(&self, mut solution: Solution<E::Value>) -> GeopResult<Solution<E::Value>> {
357 solution.enclosure = self.enclose_groups(&solution.values, &solution.solved)?;
358 Ok(solution)
359 }
360
361 /// [`System::solve`], without the enclosure: its [`Solution::enclosure`]
362 /// encloses nothing. For a solve whose answer is shown and solved
363 /// again at once — an event of a drag — where proving every group it
364 /// moved would cost several times the solve; the answer kept is proven.
365 pub fn solve_unproven(&self, start: &[E::Value]) -> GeopResult<Solution<E::Value>> {
366 self.solve_unproven_in(start, self.structure(start))
367 }
368
369 /// [`System::solve_unproven`] in the groups of `structure`: the
370 /// structure a solve of the same system found before, a hair away — an
371 /// event of a drag, the last's — rather than found again, which makes
372 /// every term twice. A pendant that cannot follow there after all is
373 /// solved with its component, as ever (see [`System::solve`]).
374 pub fn solve_unproven_in(
375 &self,
376 start: &[E::Value],
377 structure: Structure,
378 ) -> GeopResult<Solution<E::Value>> {
379 let tolerance = tolerance(self.scale)?;
380 let mut values = start.to_vec();
381 let mut solution = Solution {
382 values: Vec::new(),
383 solved: Vec::new(),
384 at_limit: Vec::new(),
385 iterations: 0,
386 phases: Vec::new(),
387 evaluations: 0,
388 enclosure: Enclosure::empty(self.mobility.len()),
389 structure: Structure::default(),
390 };
391 let mut failed: Vec<usize> = Vec::new();
392 for layer in 0..structure.layers() {
393 let groups: Vec<&Group> = (structure.layer(layer).into_iter())
394 .map(|g| &structure.groups[g])
395 .collect();
396 let base = values.clone();
397 let (ended, evaluations) = self.solve_groups(start, &base, &groups, tolerance)?;
398 solution.evaluations += evaluations;
399 for (group, ended) in groups.into_iter().zip(ended) {
400 let Some(ended) = ended else {
401 continue;
402 };
403 if !ended.holds && group.layer > 0 && !failed.contains(&group.component) {
404 failed.push(group.component);
405 }
406 solution.record(&mut values, group.clone(), ended);
407 }
408 }
409 if !failed.is_empty() {
410 // Solved again as one, from the start.
411 solution.solved.retain(|g| !failed.contains(&g.component));
412 let whole: Vec<&Group> = (failed.iter()).map(|&c| &structure.components[c]).collect();
413 for group in &whole {
414 for &b in &group.blocks {
415 values[b] = start[b].clone();
416 }
417 }
418 let base = values.clone();
419 let (ended, evaluations) = self.solve_groups(start, &base, &whole, tolerance)?;
420 solution.evaluations += evaluations;
421 for (group, ended) in whole.into_iter().zip(ended) {
422 if let Some(ended) = ended {
423 solution.record(&mut values, group.clone(), ended);
424 }
425 }
426 }
427 solution.at_limit.sort_unstable();
428 solution.at_limit.dedup();
429 solution.values = values;
430 solution.structure = structure;
431 Ok(solution)
432 }
433
434 /// The groups `groups` — of one layer — solved in lockstep, every
435 /// block outside them where `base` has it, from where the solve
436 /// started at `start`: per group where it ended, if it moved; and how
437 /// many evaluations that took.
438 fn solve_groups(
439 &self,
440 start: &[E::Value],
441 base: &[E::Value],
442 groups: &[&Group],
443 tolerance: Scalar,
444 ) -> GeopResult<Layer<E::Value>> {
445 let batch = Batch::new(self, base);
446 let ended = batch.run(groups.iter().map(|&group| {
447 let batch = &batch;
448 async move {
449 let solving = Solving::new(self, base, group, batch);
450 let (first, carried) = solving.carried(start);
451 let holds = solving.check(&first, &group.constraints, tolerance).await.1;
452 // What its own freedom does, carried, is decided by where
453 // it was held.
454 let own = carried
455 && group.own > 0
456 && (group.blocks.iter()).any(|&b| self.mobility[b] == Mobility::Held);
457 if group.preferences.is_empty() && holds && !own {
458 return Ok(carried.then_some(Ended {
459 values: first,
460 phases: Vec::new(),
461 active: Vec::new(),
462 holds,
463 }));
464 }
465 let mut active = Vec::new();
466 loop {
467 let (solved, phases) = solving.solve(first.clone(), &active, &[]).await?;
468 let beyond = solving.beyond(&solved, &active, tolerance).await;
469 if beyond.is_empty() {
470 let holds = solving
471 .check(&solved, &group.constraints, tolerance)
472 .await
473 .1;
474 return Ok(Some(Ended {
475 values: solved,
476 phases,
477 active,
478 holds,
479 }));
480 }
481 active.extend(beyond);
482 }
483 }
484 }))?;
485 Ok((ended, batch.evaluations()))
486 }
487
488 /// An enclosure of a true solution near `at` — a solution to the
489 /// solver's tolerance — group by group (see [`Enclosure`]).
490 pub fn enclose(&self, at: &[E::Value]) -> GeopResult<Enclosure<E::Value>> {
491 self.enclose_groups(at, &self.structure(at).groups)
492 }
493
494 /// `enclosure` — of the blocks at `at`, as [`Solution::enclosure`] is —
495 /// with every group it does not cover enclosed too: what a solve did
496 /// not move, where the caller needs every group (see [`System::enclose`]).
497 pub fn enclose_rest(
498 &self,
499 at: &[E::Value],
500 enclosure: Enclosure<E::Value>,
501 ) -> GeopResult<Enclosure<E::Value>> {
502 let covered: Vec<bool> = (0..self.mobility.len())
503 .map(|b| {
504 enclosure.boxes[b].is_some()
505 || (enclosure.failures.iter()).any(|(blocks, _)| blocks.contains(&b))
506 })
507 .collect();
508 let rest: Vec<Group> = (self.structure(at).groups.into_iter())
509 .filter(|group| !group.blocks.iter().all(|&b| covered[b]))
510 .collect();
511 self.enclose_into(at, &rest, enclosure)
512 }
513
514 /// How free the blocks are where they are at `at`: the directions in
515 /// which every constraint that must vanish stays put to first order —
516 /// the null space of their Jacobian — and of those, how many move each
517 /// block. Decided over the box an enclosure of the solution near `at`
518 /// proves, where one is: a redundant constraint, which looks
519 /// independent at a point a hair off the solution, does not take a
520 /// freedom away. Where none is proven, at `at`.
521 pub fn freedom(&self, at: &[E::Value]) -> GeopResult<Freedom> {
522 let enclosure = self.enclose(at)?;
523 Ok(Freedom {
524 blocks: enclosure.freedom,
525 total: enclosure.total,
526 })
527 }
528
529 /// A smallest set of the constraints that cannot all hold together —
530 /// none where they all hold after a solve from `start`, by index.
531 ///
532 /// Group by group: one whose constraints do not hold after a solve is
533 /// enclosed where it ended, and the conflict it proves names the
534 /// constraints that cannot hold together with those they depend on (see
535 /// [`EncloseError::Conflict`]). Where nothing is proven — a singular
536 /// configuration — each constraint of the group is left out in turn,
537 /// and kept out where the rest still cannot all hold: as many solves as
538 /// there are constraints, so for a dialog to show, never for every drag.
539 pub fn conflicting(&self, start: &[E::Value]) -> GeopResult<Vec<usize>> {
540 let tolerance = tolerance(self.scale)?;
541 let groups = self.structure(start).groups;
542 let batch = Batch::new(self, start);
543 let found = batch.run(groups.iter().map(|group| {
544 let batch = &batch;
545 async move { self.conflicting_in(start, group, batch, tolerance).await }
546 }))?;
547 let mut conflicting: Vec<usize> = found.into_iter().flatten().collect();
548 conflicting.sort_unstable();
549 conflicting.dedup();
550 Ok(conflicting)
551 }
552
553 /// [`System::conflicting`] of the group `group`, from `start`.
554 async fn conflicting_in(
555 &self,
556 start: &[E::Value],
557 group: &Group,
558 batch: &Batch<'_, '_, E>,
559 tolerance: Scalar,
560 ) -> GeopResult<Vec<usize>> {
561 let solving = Solving::new(self, start, group, batch);
562 let first = solving.start();
563 if (solving.check(&first, &group.constraints, tolerance).await).1 {
564 return Ok(Vec::new());
565 }
566 let holds = async |left_out: &[usize]| -> GeopResult<(Vec<E::Value>, bool)> {
567 let (solved, _) = solving.solve(first.clone(), &[], left_out).await?;
568 let kept: Vec<usize> = (group.constraints.iter())
569 .filter(|c| !left_out.contains(c))
570 .copied()
571 .collect();
572 let holds = solving.check(&solved, &kept, tolerance).await.1;
573 Ok((solved, holds))
574 };
575 let (solved, all) = holds(&[]).await?;
576 if all {
577 return Ok(Vec::new());
578 }
579 let mut at = start.to_vec();
580 for (&block, value) in group.blocks.iter().zip(&solved) {
581 at[block] = value.clone();
582 }
583 let proven = self.enclose_group(&at, group, batch).await?;
584 if let Err(EncloseError::Conflict {
585 conflicting, with, ..
586 }) = proven.boxes
587 {
588 return Ok(conflicting
589 .into_iter()
590 .map(|(c, _)| c)
591 .chain(with)
592 .collect());
593 }
594 let mut left_out = Vec::new();
595 for &c in &group.constraints {
596 left_out.push(c);
597 if holds(&left_out).await?.1 {
598 left_out.pop();
599 }
600 }
601 Ok((group.constraints.iter())
602 .filter(|c| !left_out.contains(c))
603 .copied()
604 .collect())
605 }
606}
607
608/// Per group of a layer where its solve ended, if it moved; and how many
609/// evaluations the layer took.
610type Layer<V> = (Vec<Option<Ended<V>>>, usize);
611
612/// Where a group's solve ended: its values, the phases that put them
613/// there, the limits held at their bounds, and whether its constraints
614/// hold there.
615struct Ended<V> {
616 values: Vec<V>,
617 phases: Vec<Phase>,
618 active: Vec<usize>,
619 holds: bool,
620}
621
622impl<V: Variable> Solution<V> {
623 /// `group` moved as `ended` says: its values written into `values`.
624 fn record(&mut self, values: &mut [V], group: Group, ended: Ended<V>) {
625 self.iterations = (self.iterations).max(ended.phases.iter().map(|p| p.iterations).sum());
626 worst(&mut self.phases, ended.phases);
627 for (&block, value) in group.blocks.iter().zip(ended.values) {
628 values[block] = value;
629 }
630 self.at_limit.extend(ended.active);
631 self.solved.push(group);
632 }
633}
634
635/// `phases` with each of `group`'s phases merged into the one of its
636/// index: the most steps, and the largest residual.
637fn worst(phases: &mut Vec<Phase>, group: Vec<Phase>) {
638 for (k, phase) in group.into_iter().enumerate() {
639 match phases.get_mut(k) {
640 None => phases.push(phase),
641 Some(worst) => {
642 if phase.iterations > worst.iterations {
643 worst.stop = phase.stop;
644 worst.iterations = phase.iterations;
645 }
646 if phase.max_residual.could_be_greater(worst.max_residual) {
647 worst.max_residual = phase.max_residual;
648 }
649 }
650 }
651 }
652}
653
654/// One group of a system being worked on: the blocks outside it as they
655/// stay, and where its variables are.
656struct Solving<'s, 'e, E: Equations> {
657 system: &'s System<'e, E>,
658 base: &'s [E::Value],
659 group: &'s Group,
660 /// Where its terms are asked, with every other group's.
661 batch: &'s Batch<'s, 'e, E>,
662 /// Per block of the group, where its variables start.
663 columns: Vec<usize>,
664 /// How many variables the group has.
665 n: usize,
666 space: Values<E::Value>,
667}
668
669impl<'s, 'e, E: Equations> Solving<'s, 'e, E> {
670 fn new(
671 system: &'s System<'e, E>,
672 base: &'s [E::Value],
673 group: &'s Group,
674 batch: &'s Batch<'s, 'e, E>,
675 ) -> Self {
676 let mut columns = Vec::with_capacity(group.blocks.len());
677 let mut n = 0;
678 for &block in &group.blocks {
679 columns.push(n);
680 n += base[block].dimension();
681 }
682 Self {
683 system,
684 base,
685 group,
686 batch,
687 columns,
688 n,
689 space: Values(std::marker::PhantomData),
690 }
691 }
692
693 /// The group's values where it starts.
694 fn start(&self) -> Vec<E::Value> {
695 (self.group.blocks.iter())
696 .map(|&b| self.base[b].clone())
697 .collect()
698 }
699
700 /// Where a solve of the group starts: for a pendant, carried along by
701 /// the first block it hangs off that moved since `start` and can carry
702 /// it — and whether one did; otherwise where it is.
703 fn carried(&self, start: &[E::Value]) -> (Vec<E::Value>, bool) {
704 let mut carried = false;
705 let values = (self.group.blocks.iter())
706 .map(|&b| {
707 let by = (self.group.on.iter()).find_map(|&s| {
708 (!self.base[s].same(&start[s]))
709 .then(|| self.base[b].carried(&start[s], &self.base[s]))
710 .flatten()
711 });
712 carried |= by.is_some();
713 by.unwrap_or_else(|| self.base[b].clone())
714 })
715 .collect();
716 (values, carried)
717 }
718
719 /// The terms `which` with the group at `values` (see
720 /// [`Equations::evaluate`]), asked with every other group's.
721 async fn made(&self, values: &[E::Value], which: &[Term]) -> Made {
722 self.batch.ask(&self.group.blocks, values, which).await
723 }
724
725 /// Where the variables of `block` are among the group's, if it is one
726 /// of its blocks.
727 fn column(&self, block: usize) -> Option<usize> {
728 let at = self.group.blocks.binary_search(&block).ok()?;
729 self.columns.get(at).copied()
730 }
731
732 /// The residuals of `term` as rows over the group's variables — each
733 /// block's derivatives where its variables are, each composed with
734 /// `differential` (per block of the group, `None` for the identity);
735 /// those of blocks outside the group left out.
736 fn rows<'d>(
737 &self,
738 term: &Linearized,
739 differential: &dyn Fn(usize) -> Option<&'d Vec<Vec<Scalar>>>,
740 ) -> Rows {
741 let mut rows = Rows::default();
742 for (r, &value) in term.values.iter().enumerate() {
743 let mut row = vec![Scalar::ZERO; self.n];
744 let mut set = vec![false; self.n];
745 for (block, derivatives) in &term.jacobian {
746 let (Some(column), Some(local)) = (self.column(*block), derivatives.get(r)) else {
747 continue;
748 };
749 let own = match differential(*block) {
750 None => local.clone(),
751 Some(d) => compose(local, d),
752 };
753 for (k, v) in own.into_iter().enumerate() {
754 let at = column + k;
755 if at >= self.n {
756 break;
757 }
758 // A block read twice adds its derivatives; read once,
759 // they are its own, not a zero added to.
760 row[at] = match set[at] {
761 true => row[at].add(v),
762 false => v,
763 };
764 set[at] = true;
765 }
766 }
767 rows.push(value, row);
768 }
769 rows
770 }
771
772 /// The constraints `which` with the group at `values`; `None` if one
773 /// cannot be made there.
774 async fn constraints_at(
775 &self,
776 values: &[E::Value],
777 which: &[usize],
778 ) -> Option<Vec<Linearized>> {
779 let terms: Vec<Term> = which.iter().map(|&c| Term::Constraint(c)).collect();
780 self.made(values, &terms).await.into_iter().collect()
781 }
782
783 /// The largest residual of the constraints `which` at `values`, and
784 /// whether they all hold there.
785 async fn check(
786 &self,
787 values: &[E::Value],
788 which: &[usize],
789 tolerance: Scalar,
790 ) -> (Scalar, bool) {
791 let Some(made) = self.constraints_at(values, which).await else {
792 return (Scalar::INFINITY, false);
793 };
794 let holds = (which.iter().zip(&made)).all(|(&c, made)| {
795 let bound = self.system.equations.bound(c);
796 made.values.iter().all(|&r| bound.holds(r, tolerance))
797 });
798 (
799 largest(made.iter().flat_map(|m| m.values.iter().copied())),
800 holds,
801 )
802 }
803
804 /// The limits of the group not held at their bounds that `values` is
805 /// beyond.
806 async fn beyond(&self, values: &[E::Value], active: &[usize], tolerance: Scalar) -> Vec<usize> {
807 let limits: Vec<usize> = (self.group.constraints.iter())
808 .filter(|c| !active.contains(c))
809 .filter(|&&c| self.system.equations.bound(c) == Bound::AtMost)
810 .copied()
811 .collect();
812 let Some(made) = self.constraints_at(values, &limits).await else {
813 return Vec::new();
814 };
815 (limits.iter().zip(&made))
816 .filter(|(_, made)| !(made.values.iter()).all(|&r| Bound::AtMost.holds(r, tolerance)))
817 .map(|(&c, _)| c)
818 .collect()
819 }
820}
821
822/// The row `local` times the matrix `d`: a derivative by a block's steps
823/// at a point, as one by the steps from where the retraction started.
824fn compose(local: &[Scalar], d: &[Vec<Scalar>]) -> Vec<Scalar> {
825 let columns = d.first().map_or(0, Vec::len);
826 (0..columns)
827 .map(|j| {
828 let mut sum: Option<Scalar> = None;
829 for (l, row) in local.iter().zip(d) {
830 let Some(&e) = row.get(j) else {
831 continue;
832 };
833 let term = l.mul(e);
834 sum = Some(match sum {
835 None => term,
836 Some(s) => s.add(term),
837 });
838 }
839 sum.unwrap_or(Scalar::ZERO)
840 })
841 .collect()
842}
843
844#[cfg(test)]
845mod tests;