Separate test and example for Irisawa hexlet
Put shared code in the conditionally compiled `engine::irisawa` module.
This commit is contained in:
parent
de8c662de4
commit
b23d4a1860
@ -6,6 +6,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["console_error_panic_hook"]
|
default = ["console_error_panic_hook"]
|
||||||
|
irisawa = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
@ -36,7 +37,12 @@ features = [
|
|||||||
'WebGlVertexArrayObject'
|
'WebGlVertexArrayObject'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# the self-dependency specifies features to use for tests and examples
|
||||||
|
#
|
||||||
|
# https://github.com/rust-lang/cargo/issues/2911#issuecomment-1483256987
|
||||||
|
#
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
dyna3 = { path = ".", default-features = false, features = ["irisawa"] }
|
||||||
wasm-bindgen-test = "0.3.34"
|
wasm-bindgen-test = "0.3.34"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
25
app-proto/examples/irisawa-hexlet.rs
Normal file
25
app-proto/examples/irisawa-hexlet.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use dyna3::engine::{Q, irisawa::realize_irisawa_hexlet};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
const SCALED_TOL: f64 = 1.0e-12;
|
||||||
|
let (config, success, history) = realize_irisawa_hexlet(SCALED_TOL);
|
||||||
|
print!("\nCompleted Gram matrix:{}", config.tr_mul(&*Q) * &config);
|
||||||
|
if success {
|
||||||
|
println!("Target accuracy achieved!");
|
||||||
|
} else {
|
||||||
|
println!("Failed to reach target accuracy");
|
||||||
|
}
|
||||||
|
println!("Steps: {}", history.scaled_loss.len() - 1);
|
||||||
|
println!("Loss: {}", history.scaled_loss.last().unwrap());
|
||||||
|
if success {
|
||||||
|
println!("\nChain diameters:");
|
||||||
|
println!(" {} sun (given)", 1.0 / config[(3, 3)]);
|
||||||
|
for k in 4..9 {
|
||||||
|
println!(" {} sun", 1.0 / config[(3, k)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("\nStep │ Loss\n─────┼────────────────────────────────");
|
||||||
|
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
|
||||||
|
println!("{:<4} │ {}", step, scaled_loss);
|
||||||
|
}
|
||||||
|
}
|
@ -4,5 +4,6 @@
|
|||||||
# http://xion.io/post/code/rust-examples.html
|
# http://xion.io/post/code/rust-examples.html
|
||||||
#
|
#
|
||||||
|
|
||||||
|
cargo run --example irisawa-hexlet
|
||||||
cargo run --example three-spheres
|
cargo run --example three-spheres
|
||||||
cargo run --example point-on-sphere
|
cargo run --example point-on-sphere
|
@ -279,57 +279,6 @@ pub fn realize_gram(
|
|||||||
|
|
||||||
// --- tests ---
|
// --- tests ---
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::{array, f64::consts::PI};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sub_proj_test() {
|
|
||||||
let target = PartialMatrix(vec![
|
|
||||||
MatrixEntry { index: (0, 0), value: 19.0 },
|
|
||||||
MatrixEntry { index: (0, 2), value: 39.0 },
|
|
||||||
MatrixEntry { index: (1, 1), value: 59.0 },
|
|
||||||
MatrixEntry { index: (1, 2), value: 69.0 }
|
|
||||||
]);
|
|
||||||
let attempt = DMatrix::<f64>::from_row_slice(2, 3, &[
|
|
||||||
1.0, 2.0, 3.0,
|
|
||||||
4.0, 5.0, 6.0
|
|
||||||
]);
|
|
||||||
let expected_result = DMatrix::<f64>::from_row_slice(2, 3, &[
|
|
||||||
18.0, 0.0, 36.0,
|
|
||||||
0.0, 54.0, 63.0
|
|
||||||
]);
|
|
||||||
assert_eq!(target.sub_proj(&attempt), expected_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn zero_loss_test() {
|
|
||||||
let gram = PartialMatrix({
|
|
||||||
let mut entries = Vec::<MatrixEntry>::new();
|
|
||||||
for j in 0..3 {
|
|
||||||
for k in 0..3 {
|
|
||||||
entries.push(MatrixEntry {
|
|
||||||
index: (j, k),
|
|
||||||
value: if j == k { 1.0 } else { -1.0 }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entries
|
|
||||||
});
|
|
||||||
let config = {
|
|
||||||
let a: f64 = 0.75_f64.sqrt();
|
|
||||||
DMatrix::from_columns(&[
|
|
||||||
sphere(1.0, 0.0, 0.0, a),
|
|
||||||
sphere(-0.5, a, 0.0, a),
|
|
||||||
sphere(-0.5, -a, 0.0, a)
|
|
||||||
])
|
|
||||||
};
|
|
||||||
let state = SearchState::from_config(&gram, config);
|
|
||||||
assert!(state.loss.abs() < f64::EPSILON);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this problem is from a sangaku by Irisawa Shintarō Hiroatsu. the article
|
// this problem is from a sangaku by Irisawa Shintarō Hiroatsu. the article
|
||||||
// below includes a nice translation of the problem statement, which was
|
// below includes a nice translation of the problem statement, which was
|
||||||
// recorded in Uchida Itsumi's book _Kokon sankan_ (_Mathematics, Past and
|
// recorded in Uchida Itsumi's book _Kokon sankan_ (_Mathematics, Past and
|
||||||
@ -338,7 +287,13 @@ mod tests {
|
|||||||
// "Japan's 'Wasan' Mathematical Tradition", by Abe Haruki
|
// "Japan's 'Wasan' Mathematical Tradition", by Abe Haruki
|
||||||
// https://www.nippon.com/en/japan-topics/c12801/
|
// https://www.nippon.com/en/japan-topics/c12801/
|
||||||
//
|
//
|
||||||
fn realize_irisawa_hexlet(scaled_tol: f64) -> (DMatrix<f64>, bool, DescentHistory) {
|
#[cfg(feature = "irisawa")]
|
||||||
|
pub mod irisawa {
|
||||||
|
use std::{array, f64::consts::PI};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn realize_irisawa_hexlet(scaled_tol: f64) -> (DMatrix<f64>, bool, DescentHistory) {
|
||||||
let gram = {
|
let gram = {
|
||||||
let mut gram_to_be = PartialMatrix::new();
|
let mut gram_to_be = PartialMatrix::new();
|
||||||
for s in 0..9 {
|
for s in 0..9 {
|
||||||
@ -391,12 +346,62 @@ mod tests {
|
|||||||
scaled_tol, 0.5, 0.9, 1.1, 200, 110
|
scaled_tol, 0.5, 0.9, 1.1, 200, 110
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{*, irisawa::realize_irisawa_hexlet};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sub_proj_test() {
|
||||||
|
let target = PartialMatrix(vec![
|
||||||
|
MatrixEntry { index: (0, 0), value: 19.0 },
|
||||||
|
MatrixEntry { index: (0, 2), value: 39.0 },
|
||||||
|
MatrixEntry { index: (1, 1), value: 59.0 },
|
||||||
|
MatrixEntry { index: (1, 2), value: 69.0 }
|
||||||
|
]);
|
||||||
|
let attempt = DMatrix::<f64>::from_row_slice(2, 3, &[
|
||||||
|
1.0, 2.0, 3.0,
|
||||||
|
4.0, 5.0, 6.0
|
||||||
|
]);
|
||||||
|
let expected_result = DMatrix::<f64>::from_row_slice(2, 3, &[
|
||||||
|
18.0, 0.0, 36.0,
|
||||||
|
0.0, 54.0, 63.0
|
||||||
|
]);
|
||||||
|
assert_eq!(target.sub_proj(&attempt), expected_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_loss_test() {
|
||||||
|
let gram = PartialMatrix({
|
||||||
|
let mut entries = Vec::<MatrixEntry>::new();
|
||||||
|
for j in 0..3 {
|
||||||
|
for k in 0..3 {
|
||||||
|
entries.push(MatrixEntry {
|
||||||
|
index: (j, k),
|
||||||
|
value: if j == k { 1.0 } else { -1.0 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries
|
||||||
|
});
|
||||||
|
let config = {
|
||||||
|
let a: f64 = 0.75_f64.sqrt();
|
||||||
|
DMatrix::from_columns(&[
|
||||||
|
sphere(1.0, 0.0, 0.0, a),
|
||||||
|
sphere(-0.5, a, 0.0, a),
|
||||||
|
sphere(-0.5, -a, 0.0, a)
|
||||||
|
])
|
||||||
|
};
|
||||||
|
let state = SearchState::from_config(&gram, config);
|
||||||
|
assert!(state.loss.abs() < f64::EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn irisawa_hexlet_test() {
|
fn irisawa_hexlet_test() {
|
||||||
// solve Irisawa's problem
|
// solve Irisawa's problem
|
||||||
const SCALED_TOL: f64 = 1.0e-12;
|
const SCALED_TOL: f64 = 1.0e-12;
|
||||||
let (config, success, history) = realize_irisawa_hexlet(SCALED_TOL);
|
let (config, _, _) = realize_irisawa_hexlet(SCALED_TOL);
|
||||||
|
|
||||||
// check against Irisawa's solution
|
// check against Irisawa's solution
|
||||||
let entry_tol = SCALED_TOL.sqrt();
|
let entry_tol = SCALED_TOL.sqrt();
|
||||||
@ -404,27 +409,6 @@ mod tests {
|
|||||||
for (k, diam) in solution_diams.into_iter().enumerate() {
|
for (k, diam) in solution_diams.into_iter().enumerate() {
|
||||||
assert!((config[(3, k)] - 1.0 / diam).abs() < entry_tol);
|
assert!((config[(3, k)] - 1.0 / diam).abs() < entry_tol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// print info
|
|
||||||
print!("\nCompleted Gram matrix:{}", config.tr_mul(&*Q) * &config);
|
|
||||||
if success {
|
|
||||||
println!("Target accuracy achieved!");
|
|
||||||
} else {
|
|
||||||
println!("Failed to reach target accuracy");
|
|
||||||
}
|
|
||||||
println!("Steps: {}", history.scaled_loss.len() - 1);
|
|
||||||
println!("Loss: {}", history.scaled_loss.last().unwrap());
|
|
||||||
if success {
|
|
||||||
println!("\nChain diameters:");
|
|
||||||
println!(" {} sun (given)", 1.0 / config[(3, 3)]);
|
|
||||||
for k in 4..9 {
|
|
||||||
println!(" {} sun", 1.0 / config[(3, k)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("\nStep │ Loss\n─────┼────────────────────────────────");
|
|
||||||
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
|
|
||||||
println!("{:<4} │ {}", step, scaled_loss);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// at the frozen indices, the optimization steps should have exact zeros,
|
// at the frozen indices, the optimization steps should have exact zeros,
|
||||||
|
Loading…
Reference in New Issue
Block a user