Encapsulate realization results
In the process, spruce up our realization diagnostics logging and factor out some of the repetitive code in the examples, because we're already changing those parts of the code to adapt them to the new encapsulation. This commit changes the example output format. I've checked by hand that the output is rearranged but not meaningfully changed.
This commit is contained in:
parent
4cb3262555
commit
679c421d04
8 changed files with 176 additions and 103 deletions
|
@ -393,6 +393,16 @@ fn seek_better_config(
|
|||
None
|
||||
}
|
||||
|
||||
pub struct Realization {
|
||||
pub config: DMatrix<f64>,
|
||||
pub tangent: ConfigSubspace
|
||||
}
|
||||
|
||||
pub struct RealizationResult {
|
||||
pub result: Result<Realization, String>,
|
||||
pub history: DescentHistory
|
||||
}
|
||||
|
||||
// seek a matrix `config` that matches the partial matrix `problem.frozen` and
|
||||
// has `config' * Q * config` matching the partial matrix `problem.gram`. start
|
||||
// at `problem.guess`, set the frozen entries to their desired values, and then
|
||||
|
@ -405,7 +415,7 @@ pub fn realize_gram(
|
|||
reg_scale: f64,
|
||||
max_descent_steps: i32,
|
||||
max_backoff_steps: i32
|
||||
) -> (DMatrix<f64>, ConfigSubspace, bool, DescentHistory) {
|
||||
) -> RealizationResult {
|
||||
// destructure the problem data
|
||||
let ConstraintProblem {
|
||||
gram, guess, frozen
|
||||
|
@ -491,19 +501,20 @@ pub fn realize_gram(
|
|||
history.base_step.push(base_step.clone());
|
||||
|
||||
// use backtracking line search to find a better configuration
|
||||
match seek_better_config(
|
||||
if let Some((better_state, backoff_steps)) = seek_better_config(
|
||||
gram, &state, &base_step, neg_grad.dot(&base_step),
|
||||
min_efficiency, backoff, max_backoff_steps
|
||||
) {
|
||||
Some((better_state, backoff_steps)) => {
|
||||
state = better_state;
|
||||
history.backoff_steps.push(backoff_steps);
|
||||
},
|
||||
None => return (state.config, ConfigSubspace::zero(assembly_dim), false, history)
|
||||
state = better_state;
|
||||
history.backoff_steps.push(backoff_steps);
|
||||
} else {
|
||||
return RealizationResult {
|
||||
result: Err("Line search failed".to_string()),
|
||||
history
|
||||
}
|
||||
};
|
||||
}
|
||||
let success = state.loss < tol;
|
||||
let tangent = if success {
|
||||
let result = if state.loss < tol {
|
||||
// express the uniform basis in the standard basis
|
||||
const UNIFORM_DIM: usize = 4;
|
||||
let total_dim_unif = UNIFORM_DIM * assembly_dim;
|
||||
|
@ -516,11 +527,13 @@ pub fn realize_gram(
|
|||
}
|
||||
|
||||
// find the kernel of the Hessian. give it the uniform inner product
|
||||
ConfigSubspace::symmetric_kernel(hess, unif_to_std, assembly_dim)
|
||||
let tangent = ConfigSubspace::symmetric_kernel(hess, unif_to_std, assembly_dim);
|
||||
|
||||
Ok(Realization { config: state.config, tangent })
|
||||
} else {
|
||||
ConfigSubspace::zero(assembly_dim)
|
||||
Err("Failed to reach target accuracy".to_string())
|
||||
};
|
||||
(state.config, tangent, success, history)
|
||||
RealizationResult{ result, history }
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
@ -539,7 +552,7 @@ pub mod examples {
|
|||
// "Japan's 'Wasan' Mathematical Tradition", by Abe Haruki
|
||||
// https://www.nippon.com/en/japan-topics/c12801/
|
||||
//
|
||||
pub fn realize_irisawa_hexlet(scaled_tol: f64) -> (DMatrix<f64>, ConfigSubspace, bool, DescentHistory) {
|
||||
pub fn realize_irisawa_hexlet(scaled_tol: f64) -> RealizationResult {
|
||||
let mut problem = ConstraintProblem::from_guess(
|
||||
[
|
||||
sphere(0.0, 0.0, 0.0, 15.0),
|
||||
|
@ -590,7 +603,7 @@ pub mod examples {
|
|||
|
||||
// set up a kaleidocycle, made of points with fixed distances between them,
|
||||
// and find its tangent space
|
||||
pub fn realize_kaleidocycle(scaled_tol: f64) -> (DMatrix<f64>, ConfigSubspace, bool, DescentHistory) {
|
||||
pub fn realize_kaleidocycle(scaled_tol: f64) -> RealizationResult {
|
||||
const N_HINGES: usize = 6;
|
||||
let mut problem = ConstraintProblem::from_guess(
|
||||
(0..N_HINGES).step_by(2).flat_map(
|
||||
|
@ -714,10 +727,10 @@ mod tests {
|
|||
}
|
||||
problem.frozen.push(3, 0, problem.guess[(3, 0)]);
|
||||
problem.frozen.push(3, 1, 0.5);
|
||||
let (config, _, success, history) = realize_gram(
|
||||
let RealizationResult { result, history } = realize_gram(
|
||||
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
assert_eq!(success, true);
|
||||
let config = result.unwrap().config;
|
||||
for base_step in history.base_step.into_iter() {
|
||||
for &MatrixEntry { index, .. } in &problem.frozen {
|
||||
assert_eq!(base_step[index], 0.0);
|
||||
|
@ -732,7 +745,7 @@ mod tests {
|
|||
fn irisawa_hexlet_test() {
|
||||
// solve Irisawa's problem
|
||||
const SCALED_TOL: f64 = 1.0e-12;
|
||||
let (config, _, _, _) = realize_irisawa_hexlet(SCALED_TOL);
|
||||
let config = realize_irisawa_hexlet(SCALED_TOL).result.unwrap().config;
|
||||
|
||||
// check against Irisawa's solution
|
||||
let entry_tol = SCALED_TOL.sqrt();
|
||||
|
@ -759,11 +772,11 @@ mod tests {
|
|||
for n in 0..ELEMENT_DIM {
|
||||
problem.frozen.push(n, 0, problem.guess[(n, 0)]);
|
||||
}
|
||||
let (config, tangent, success, history) = realize_gram(
|
||||
let RealizationResult { result, history } = realize_gram(
|
||||
&problem, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
let Realization { config, tangent } = result.unwrap();
|
||||
assert_eq!(config, problem.guess);
|
||||
assert_eq!(success, true);
|
||||
assert_eq!(history.scaled_loss.len(), 1);
|
||||
|
||||
// list some motions that should form a basis for the tangent space of
|
||||
|
@ -831,8 +844,8 @@ mod tests {
|
|||
fn tangent_test_kaleidocycle() {
|
||||
// set up a kaleidocycle and find its tangent space
|
||||
const SCALED_TOL: f64 = 1.0e-12;
|
||||
let (config, tangent, success, history) = realize_kaleidocycle(SCALED_TOL);
|
||||
assert_eq!(success, true);
|
||||
let RealizationResult { result, history } = realize_kaleidocycle(SCALED_TOL);
|
||||
let Realization { config, tangent } = result.unwrap();
|
||||
assert_eq!(history.scaled_loss.len(), 1);
|
||||
|
||||
// list some motions that should form a basis for the tangent space of
|
||||
|
@ -920,11 +933,11 @@ mod tests {
|
|||
problem_orig.gram.push_sym(0, 0, 1.0);
|
||||
problem_orig.gram.push_sym(1, 1, 1.0);
|
||||
problem_orig.gram.push_sym(0, 1, 0.5);
|
||||
let (config_orig, tangent_orig, success_orig, history_orig) = realize_gram(
|
||||
let RealizationResult { result: result_orig, history: history_orig } = realize_gram(
|
||||
&problem_orig, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
let Realization { config: config_orig, tangent: tangent_orig } = result_orig.unwrap();
|
||||
assert_eq!(config_orig, problem_orig.guess);
|
||||
assert_eq!(success_orig, true);
|
||||
assert_eq!(history_orig.scaled_loss.len(), 1);
|
||||
|
||||
// find another pair of spheres that meet at 120°. we'll think of this
|
||||
|
@ -941,11 +954,11 @@ mod tests {
|
|||
guess: guess_tfm,
|
||||
frozen: problem_orig.frozen
|
||||
};
|
||||
let (config_tfm, tangent_tfm, success_tfm, history_tfm) = realize_gram(
|
||||
let RealizationResult { result: result_tfm, history: history_tfm } = realize_gram(
|
||||
&problem_tfm, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
let Realization { config: config_tfm, tangent: tangent_tfm } = result_tfm.unwrap();
|
||||
assert_eq!(config_tfm, problem_tfm.guess);
|
||||
assert_eq!(success_tfm, true);
|
||||
assert_eq!(history_tfm.scaled_loss.len(), 1);
|
||||
|
||||
// project a nudge to the tangent space of the solution variety at the
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue