Compare commits

..

No commits in common. "0be7448e2475b990572ed3c7989eaa52eabf980b" and "a4d081f684cb3b917c49736f85c6564d0fd96c31" have entirely different histories.

16 changed files with 141 additions and 1059 deletions

565
app-proto/Cargo.lock generated
View file

@ -20,21 +20,6 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "approx" name = "approx"
version = "0.5.1" version = "0.5.1"
@ -50,21 +35,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.16.0" version = "3.16.0"
@ -98,35 +68,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "charming"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ffae2e616ae7d66b2e9ea369f1c7650042bdcdc1dc08b04b027107007b4f09"
dependencies = [
"handlebars",
"js-sys",
"serde",
"serde-wasm-bindgen",
"serde_json",
"serde_with",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"serde",
"windows-link",
]
[[package]] [[package]]
name = "console_error_panic_hook" name = "console_error_panic_hook"
version = "0.1.7" version = "0.1.7"
@ -137,122 +78,10 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "deranged"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
"serde",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]] [[package]]
name = "dyna3" name = "dyna3"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"charming",
"console_error_panic_hook", "console_error_panic_hook",
"dyna3", "dyna3",
"itertools", "itertools",
@ -277,22 +106,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -304,28 +117,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "handlebars"
version = "6.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098"
dependencies = [
"derive_builder",
"log",
"num-order",
"pest",
"pest_derive",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.14.5"
@ -336,12 +127,6 @@ dependencies = [
"allocator-api2", "allocator-api2",
] ]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "html-escape" name = "html-escape"
version = "0.2.13" version = "0.2.13"
@ -351,47 +136,6 @@ dependencies = [
"utf8-width", "utf8-width",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"serde",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.5.0" version = "2.5.0"
@ -399,8 +143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.5", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@ -412,12 +155,6 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.70" version = "0.3.70"
@ -455,12 +192,6 @@ dependencies = [
"rawpointer", "rawpointer",
] ]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]] [[package]]
name = "minicov" name = "minicov"
version = "0.3.5" version = "0.3.5"
@ -517,12 +248,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.46" version = "0.1.46"
@ -532,21 +257,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-modular"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
[[package]]
name = "num-order"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6"
dependencies = [
"num-modular",
]
[[package]] [[package]]
name = "num-rational" name = "num-rational"
version = "0.4.2" version = "0.4.2"
@ -579,57 +289,6 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pest"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.20" version = "0.2.20"
@ -704,12 +363,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]] [[package]]
name = "safe_arch" name = "safe_arch"
version = "0.7.2" version = "0.7.2"
@ -734,90 +387,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_with"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
dependencies = [
"base64",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.5.0",
"serde",
"serde_derive",
"serde_json",
"serde_with_macros",
"time",
]
[[package]]
name = "serde_with_macros"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@ -852,20 +421,14 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "sycamore" name = "sycamore"
version = "0.9.1" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f38201dcb10aa609e81ca6f7547758a7eb602240a5ff682e668909fd0f7b2cc" checksum = "5f38201dcb10aa609e81ca6f7547758a7eb602240a5ff682e668909fd0f7b2cc"
dependencies = [ dependencies = [
"hashbrown 0.14.5", "hashbrown",
"indexmap 2.5.0", "indexmap",
"paste", "paste",
"sycamore-core", "sycamore-core",
"sycamore-macro", "sycamore-macro",
@ -881,7 +444,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41dc04bf0de321c6486356b2be751fac82fabb06c992d25b6748587561bba187" checksum = "41dc04bf0de321c6486356b2be751fac82fabb06c992d25b6748587561bba187"
dependencies = [ dependencies = [
"hashbrown 0.14.5", "hashbrown",
"paste", "paste",
"sycamore-reactive", "sycamore-reactive",
] ]
@ -943,78 +506,21 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.87" version = "2.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.13"
@ -1171,65 +677,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"

View file

@ -17,9 +17,6 @@ nalgebra = "0.33.0"
readonly = "0.2.12" readonly = "0.2.12"
sycamore = "0.9.1" sycamore = "0.9.1"
# We use Charming to help display engine diagnostics
charming = { version = "0.5.1", features = ["wasm"] }
# The `console_error_panic_hook` crate provides better debugging of panics by # The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires # logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for

View file

@ -1,36 +0,0 @@
#![allow(dead_code)]
use nalgebra::DMatrix;
use dyna3::engine::{Q, DescentHistory, RealizationResult};
pub fn print_title(title: &str) {
println!("─── {title} ───");
}
pub fn print_realization_diagnostics(realization_result: &RealizationResult) {
let RealizationResult { result, history } = realization_result;
println!();
if let Err(ref message) = result {
println!("❌️ {message}");
} else {
println!("✅️ Target accuracy achieved!");
}
println!("Steps: {}", history.scaled_loss.len() - 1);
println!("Loss: {}", history.scaled_loss.last().unwrap());
}
pub fn print_gram_matrix(config: &DMatrix<f64>) {
println!("\nCompleted Gram matrix:{}", (config.tr_mul(&*Q) * config).to_string().trim_end());
}
pub fn print_config(config: &DMatrix<f64>) {
println!("\nConfiguration:{}", config.to_string().trim_end());
}
pub fn print_loss_history(history: &DescentHistory) {
println!("\nStep │ Loss\n─────┼────────────────────────────────");
for (step, scaled_loss) in history.scaled_loss.iter().enumerate() {
println!("{:<4}{}", step, scaled_loss);
}
}

View file

@ -1,28 +1,25 @@
mod common; use dyna3::engine::{Q, examples::realize_irisawa_hexlet};
use common::{
print_gram_matrix,
print_loss_history,
print_realization_diagnostics,
print_title
};
use dyna3::engine::{Realization, examples::realize_irisawa_hexlet};
fn main() { fn main() {
const SCALED_TOL: f64 = 1.0e-12; const SCALED_TOL: f64 = 1.0e-12;
let realization_result = realize_irisawa_hexlet(SCALED_TOL); let (config, _, success, history) = realize_irisawa_hexlet(SCALED_TOL);
print_title("Irisawa hexlet"); print!("\nCompleted Gram matrix:{}", config.tr_mul(&*Q) * &config);
print_realization_diagnostics(&realization_result); if success {
if let Ok(Realization { config, .. }) = realization_result.result { println!("Target accuracy achieved!");
// print the diameters of the chain spheres } 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!("\nChain diameters:");
println!(" {} sun (given)", 1.0 / config[(3, 3)]); println!(" {} sun (given)", 1.0 / config[(3, 3)]);
for k in 4..9 { for k in 4..9 {
println!(" {} sun", 1.0 / config[(3, k)]); println!(" {} sun", 1.0 / config[(3, k)]);
} }
// print the completed Gram matrix
print_gram_matrix(&config);
} }
print_loss_history(&realization_result.history); println!("\nStep │ Loss\n─────┼────────────────────────────────");
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
println!("{:<4}{}", step, scaled_loss);
}
} }

View file

@ -1,37 +1,30 @@
mod common;
use nalgebra::{DMatrix, DVector}; use nalgebra::{DMatrix, DVector};
use common::{ use dyna3::engine::{Q, examples::realize_kaleidocycle};
print_config,
print_gram_matrix,
print_realization_diagnostics,
print_title
};
use dyna3::engine::{Realization, examples::realize_kaleidocycle};
fn main() { fn main() {
const SCALED_TOL: f64 = 1.0e-12; const SCALED_TOL: f64 = 1.0e-12;
let realization_result = realize_kaleidocycle(SCALED_TOL); let (config, tangent, success, history) = realize_kaleidocycle(SCALED_TOL);
print_title("Kaleidocycle"); print!("Completed Gram matrix:{}", config.tr_mul(&*Q) * &config);
print_realization_diagnostics(&realization_result); print!("Configuration:{}", config);
if let Ok(Realization { config, tangent }) = realization_result.result { if success {
// print the completed Gram matrix and the realized configuration println!("Target accuracy achieved!");
print_gram_matrix(&config); } else {
print_config(&config); println!("Failed to reach target accuracy");
// find the kaleidocycle's twist motion by projecting onto the tangent
// space
const N_POINTS: usize = 12;
let up = DVector::from_column_slice(&[0.0, 0.0, 1.0, 0.0]);
let down = -&up;
let twist_motion: DMatrix<_> = (0..N_POINTS).step_by(4).flat_map(
|n| [
tangent.proj(&up.as_view(), n),
tangent.proj(&down.as_view(), n+1)
]
).sum();
let normalization = 5.0 / twist_motion[(2, 0)];
println!("\nTwist motion:{}", (normalization * twist_motion).to_string().trim_end());
} }
println!("Steps: {}", history.scaled_loss.len() - 1);
println!("Loss: {}\n", history.scaled_loss.last().unwrap());
// find the kaleidocycle's twist motion by projecting onto the tangent space
const N_POINTS: usize = 12;
let up = DVector::from_column_slice(&[0.0, 0.0, 1.0, 0.0]);
let down = -&up;
let twist_motion: DMatrix<_> = (0..N_POINTS).step_by(4).flat_map(
|n| [
tangent.proj(&up.as_view(), n),
tangent.proj(&down.as_view(), n+1)
]
).sum();
let normalization = 5.0 / twist_motion[(2, 0)];
print!("Twist motion:{}", normalization * twist_motion);
} }

View file

@ -1,19 +1,4 @@
mod common; use dyna3::engine::{Q, point, realize_gram, sphere, ConstraintProblem};
use common::{
print_config,
print_gram_matrix,
print_loss_history,
print_realization_diagnostics,
print_title
};
use dyna3::engine::{
point,
realize_gram,
sphere,
ConstraintProblem,
Realization
};
fn main() { fn main() {
let mut problem = ConstraintProblem::from_guess(&[ let mut problem = ConstraintProblem::from_guess(&[
@ -26,14 +11,21 @@ fn main() {
} }
} }
problem.frozen.push(3, 0, problem.guess[(3, 0)]); problem.frozen.push(3, 0, problem.guess[(3, 0)]);
let realization_result = realize_gram( println!();
let (config, _, success, history) = realize_gram(
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110 &problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
); );
print_title("Point on a sphere"); print!("\nCompleted Gram matrix:{}", config.tr_mul(&*Q) * &config);
print_realization_diagnostics(&realization_result); print!("Configuration:{}", config);
if let Ok(Realization{ config, .. }) = realization_result.result { if success {
print_gram_matrix(&config); println!("Target accuracy achieved!");
print_config(&config); } else {
println!("Failed to reach target accuracy");
}
println!("Steps: {}", history.scaled_loss.len() - 1);
println!("Loss: {}", history.scaled_loss.last().unwrap());
println!("\nStep │ Loss\n─────┼────────────────────────────────");
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
println!("{:<4}{}", step, scaled_loss);
} }
print_loss_history(&realization_result.history);
} }

View file

@ -1,12 +1,4 @@
mod common; use dyna3::engine::{Q, realize_gram, sphere, ConstraintProblem};
use common::{
print_gram_matrix,
print_loss_history,
print_realization_diagnostics,
print_title
};
use dyna3::engine::{realize_gram, sphere, ConstraintProblem, Realization};
fn main() { fn main() {
let mut problem = ConstraintProblem::from_guess({ let mut problem = ConstraintProblem::from_guess({
@ -22,13 +14,20 @@ fn main() {
problem.gram.push_sym(j, k, if j == k { 1.0 } else { -1.0 }); problem.gram.push_sym(j, k, if j == k { 1.0 } else { -1.0 });
} }
} }
let realization_result = realize_gram( println!();
let (config, _, success, history) = realize_gram(
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110 &problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
); );
print_title("Three spheres"); print!("\nCompleted Gram matrix:{}", config.tr_mul(&*Q) * &config);
print_realization_diagnostics(&realization_result); if success {
if let Ok(Realization{ config, .. }) = realization_result.result { println!("Target accuracy achieved!");
print_gram_matrix(&config); } else {
println!("Failed to reach target accuracy");
}
println!("Steps: {}", history.scaled_loss.len() - 1);
println!("Loss: {}", history.scaled_loss.last().unwrap());
println!("\nStep │ Loss\n─────┼────────────────────────────────");
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
println!("{:<4}{}", step, scaled_loss);
} }
print_loss_history(&realization_result.history);
} }

View file

@ -6,12 +6,6 @@
<link data-trunk rel="css" href="main.css"/> <link data-trunk rel="css" href="main.css"/>
<link href="https://fonts.bunny.net/css?family=fira-sans:ital,wght@0,400;1,400&display=swap" rel="stylesheet"> <link href="https://fonts.bunny.net/css?family=fira-sans:ital,wght@0,400;1,400&display=swap" rel="stylesheet">
<link href="https://fonts.bunny.net/css?family=noto-emoji:wght@400&text=%f0%9f%94%97%e2%9a%a0&display=swap" rel="stylesheet"> <link href="https://fonts.bunny.net/css?family=noto-emoji:wght@400&text=%f0%9f%94%97%e2%9a%a0&display=swap" rel="stylesheet">
<!--
the Charming visualization crate, which we use to show engine diagnostics,
depends the ECharts JavaScript package
-->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>
</head> </head>
<body></body> <body></body>
</html> </html>

View file

@ -18,17 +18,6 @@ body {
font-family: 'Fira Sans', sans-serif; font-family: 'Fira Sans', sans-serif;
} }
.invalid {
color: var(--text-invalid);
}
.status {
width: 20px;
text-align: center;
font-family: 'Noto Emoji';
font-style: normal;
}
/* sidebar */ /* sidebar */
#sidebar { #sidebar {
@ -149,7 +138,6 @@ details[open]:has(li) .element-switch::after {
} }
.regulator-input { .regulator-input {
margin-right: 4px;
color: inherit; color: inherit;
background-color: inherit; background-color: inherit;
border: 1px solid var(--border); border: 1px solid var(--border);
@ -171,56 +159,22 @@ details[open]:has(li) .element-switch::after {
border-color: var(--border-invalid); border-color: var(--border-invalid);
} }
.status {
width: 20px;
padding-left: 4px;
text-align: center;
font-family: 'Noto Emoji';
font-style: normal;
}
.regulator-input.invalid + .status::after, details:has(.invalid):not([open]) .status::after { .regulator-input.invalid + .status::after, details:has(.invalid):not([open]) .status::after {
content: '⚠'; content: '⚠';
color: var(--text-invalid); color: var(--text-invalid);
} }
/* diagnostics */
#diagnostics {
margin: 10px;
}
#diagnostics-bar {
display: flex;
}
#realization-status {
display: flex;
flex-grow: 1;
}
#realization-status .status {
margin-right: 4px;
}
#realization-status :not(.status) {
flex-grow: 1;
}
#realization-status .status::after {
content: '✓';
}
#realization-status.invalid .status::after {
content: '⚠';
}
.diagnostics-panel {
margin-top: 10px;
min-height: 180px;
}
.diagnostics-chart {
background-color: var(--display-background);
border: 1px solid var(--border);
border-radius: 8px;
}
/* display */ /* display */
#display { canvas {
float: left; float: left;
margin-left: 20px; margin-left: 20px;
margin-top: 20px; margin-top: 20px;
@ -229,7 +183,7 @@ details[open]:has(li) .element-switch::after {
border-radius: 16px; border-radius: 16px;
} }
#display:focus { canvas:focus {
border-color: var(--border-focus-dark); border-color: var(--border-focus-dark);
outline: none; outline: none;
} }

View file

@ -6,7 +6,7 @@
# http://xion.io/post/code/rust-examples.html # http://xion.io/post/code/rust-examples.html
# #
cargo run --example irisawa-hexlet; echo cargo run --example irisawa-hexlet
cargo run --example three-spheres; echo cargo run --example three-spheres
cargo run --example point-on-sphere; echo cargo run --example point-on-sphere
cargo run --example kaleidocycle cargo run --example kaleidocycle

View file

@ -3,9 +3,8 @@ use sycamore::prelude::*;
use web_sys::{console, wasm_bindgen::JsValue}; use web_sys::{console, wasm_bindgen::JsValue};
use crate::{ use crate::{
AppState,
engine, engine,
engine::DescentHistory, AppState,
assembly::{Assembly, InversiveDistanceRegulator, Point, Sphere} assembly::{Assembly, InversiveDistanceRegulator, Point, Sphere}
}; };
@ -196,7 +195,6 @@ pub fn AddRemove() -> View {
assembly.regulators.update(|regs| regs.clear()); assembly.regulators.update(|regs| regs.clear());
assembly.elements.update(|elts| elts.clear()); assembly.elements.update(|elts| elts.clear());
assembly.elements_by_id.update(|elts_by_id| elts_by_id.clear()); assembly.elements_by_id.update(|elts_by_id| elts_by_id.clear());
assembly.descent_history.set(DescentHistory::new());
state.selection.update(|sel| sel.clear()); state.selection.update(|sel| sel.clear());
// load assembly // load assembly

View file

@ -24,10 +24,7 @@ use crate::{
realize_gram, realize_gram,
sphere, sphere,
ConfigSubspace, ConfigSubspace,
ConstraintProblem, ConstraintProblem
DescentHistory,
Realization,
RealizationResult
}, },
outline::OutlineItem, outline::OutlineItem,
specified::SpecifiedValue specified::SpecifiedValue
@ -550,11 +547,7 @@ pub struct Assembly {
pub tangent: Signal<ConfigSubspace>, pub tangent: Signal<ConfigSubspace>,
// indexing // indexing
pub elements_by_id: Signal<BTreeMap<String, Rc<dyn Element>>>, pub elements_by_id: Signal<BTreeMap<String, Rc<dyn Element>>>
// realization diagnostics
pub realization_status: Signal<Result<(), String>>,
pub descent_history: Signal<DescentHistory>
} }
impl Assembly { impl Assembly {
@ -563,9 +556,7 @@ impl Assembly {
elements: create_signal(BTreeSet::new()), elements: create_signal(BTreeSet::new()),
regulators: create_signal(BTreeSet::new()), regulators: create_signal(BTreeSet::new()),
tangent: create_signal(ConfigSubspace::zero(0)), tangent: create_signal(ConfigSubspace::zero(0)),
elements_by_id: create_signal(BTreeMap::default()), elements_by_id: create_signal(BTreeMap::default())
realization_status: create_signal(Ok(())),
descent_history: create_signal(DescentHistory::new())
} }
} }
@ -696,49 +687,31 @@ impl Assembly {
console_log!("Old configuration:{:>8.3}", problem.guess); console_log!("Old configuration:{:>8.3}", problem.guess);
// look for a configuration with the given Gram matrix // look for a configuration with the given Gram matrix
let RealizationResult { result, history } = realize_gram( let (config, tangent, success, history) = realize_gram(
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110 &problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
); );
/* DEBUG */ /* DEBUG */
// report the outcome of the search in the browser console // report the outcome of the search
if let Err(ref message) = result { if success {
console_log!("❌️ {message}"); console_log!("Target accuracy achieved!")
} else { } else {
console_log!("✅️ Target accuracy achieved!"); console_log!("Failed to reach target accuracy")
} }
console_log!("Steps: {}", history.scaled_loss.len() - 1); console_log!("Steps: {}", history.scaled_loss.len() - 1);
console_log!("Loss: {}", history.scaled_loss.last().unwrap()); console_log!("Loss: {}", *history.scaled_loss.last().unwrap());
console_log!("Tangent dimension: {}", tangent.dim());
// report the loss history if success {
self.descent_history.set(history); // read out the solution
for elt in self.elements.get_clone_untracked() {
match result { elt.representation().update(
Ok(Realization { config, tangent }) => { |rep| rep.set_column(0, &config.column(elt.column_index().unwrap()))
/* DEBUG */ );
// report the tangent dimension
console_log!("Tangent dimension: {}", tangent.dim());
// report the realization status
self.realization_status.set(Ok(()));
// read out the solution
for elt in self.elements.get_clone_untracked() {
elt.representation().update(
|rep| rep.set_column(0, &config.column(elt.column_index().unwrap()))
);
}
// save the tangent space
self.tangent.set_silent(tangent);
},
Err(message) => {
// report the realization status. the `Err(message)` we're
// setting the status to has a different type than the
// `Err(message)` we received from the match: we're changing the
// `Ok` type from `Realization` to `()`
self.realization_status.set(Err(message))
} }
// save the tangent space
self.tangent.set_silent(tangent);
} }
} }

View file

@ -1,199 +0,0 @@
use charming::{
Chart,
WasmRenderer,
component::{Axis, Grid},
element::AxisType,
series::{Line, Scatter},
};
use sycamore::prelude::*;
use crate::AppState;
#[derive(Clone)]
struct DiagnosticsState {
active_tab: Signal<String>
}
impl DiagnosticsState {
fn new(initial_tab: String) -> DiagnosticsState {
DiagnosticsState {
active_tab: create_signal(initial_tab)
}
}
}
// a realization status indicator
#[component]
fn RealizationStatus() -> View {
let state = use_context::<AppState>();
let realization_status = state.assembly.realization_status;
view! {
div(
id="realization-status",
class=realization_status.with(
|status| match status {
Ok(_) => "",
Err(_) => "invalid"
}
)
) {
div(class="status")
div {
(realization_status.with(
|status| match status {
Ok(_) => "Target accuracy achieved".to_string(),
Err(message) => message.clone()
}
))
}
}
}
}
// the loss history from the last realization
#[component]
fn LossHistory() -> View {
const CONTAINER_ID: &str = "loss-history";
let state = use_context::<AppState>();
let renderer = WasmRenderer::new_opt(None, Some(178));
on_mount(move || {
create_effect(move || {
// get the loss history
let scaled_loss = state.assembly.descent_history.with(
|history| history.scaled_loss.clone()
);
let step_cnt = scaled_loss.len();
// initialize the chart axes and series
const MIN_INTERVAL: f64 = 0.01;
let mut step_axis = Axis::new()
.type_(AxisType::Category)
.boundary_gap(false);
let scaled_loss_axis = Axis::new()
.type_(AxisType::Value)
.min(0)
.min_interval(MIN_INTERVAL);
// load the chart data. when there's no history, we load the data
// point (0, None) to clear the chart. it would feel more natural to
// load empty data vectors, but that turns out not to clear the
// chart: it instead leads to previous data being re-used
let mut scaled_loss_series = Line::new();
if step_cnt > 0 {
step_axis = step_axis.data(
(0..step_cnt).map(|step| step.to_string()).collect()
);
scaled_loss_series = scaled_loss_series.data(scaled_loss);
} else {
step_axis = step_axis.data(vec![0.to_string()]);
scaled_loss_series = scaled_loss_series.data(vec![None::<f64>]);
}
let chart = Chart::new()
.animation(false)
.x_axis(step_axis)
.y_axis(scaled_loss_axis)
.grid(Grid::new().top(20).right(40).bottom(30).left(60))
.series(scaled_loss_series);
renderer.render(CONTAINER_ID, &chart).unwrap();
});
});
view! {
div(id=CONTAINER_ID, class="diagnostics-chart")
}
}
// the spectrum of the Hessian during the last realization
#[component]
fn SpectrumHistory() -> View {
const CONTAINER_ID: &str = "spectrum-history";
let state = use_context::<AppState>();
let renderer = WasmRenderer::new(478, 178);
on_mount(move || {
create_effect(move || {
// get the spectrum of the Hessian at each step
let hess_eigvals = state.assembly.descent_history.with(
|history| history.hess_eigvals
.iter()
.enumerate()
.map(
|(step, eigvals)| eigvals.iter().map(
move |val| vec![step as f64, *val]
)
)
.flatten()
.collect::<Vec<_>>()
);
// initialize the chart axes and series
let step_axis = Axis::new();
let eigval_axis = Axis::new();
// load the chart data. when there's no history, we load the data
// point (0, None) to clear the chart. it would feel more natural to
// load empty data vectors, but that turns out not to clear the
// chart: it instead leads to previous data being re-used
let mut eigval_series = Scatter::new().symbol_size(7);
if hess_eigvals.len() > 0 {
eigval_series = eigval_series.data(hess_eigvals);
} else {
eigval_series = eigval_series.data(vec![None::<f64>, None::<f64>]);
}
let chart = Chart::new()
.animation(false)
.x_axis(step_axis)
.y_axis(eigval_axis)
.grid(Grid::new().top(20).right(40).bottom(30).left(60))
.series(eigval_series);
renderer.render(CONTAINER_ID, &chart).unwrap();
});
});
view! {
div(id=CONTAINER_ID, class="diagnostics-chart")
}
}
#[component(inline_props)]
fn DiagnosticsPanel(name: &'static str, children: Children) -> View {
let diagnostics_state = use_context::<DiagnosticsState>();
view! {
div(
class="diagnostics-panel",
"hidden"=diagnostics_state.active_tab.with(
|active_tab| {
if active_tab == name {
None
} else {
Some("")
}
}
)
) {
(children)
}
}
}
#[component]
pub fn Diagnostics() -> View {
let diagnostics_state = DiagnosticsState::new("loss".to_string());
let active_tab = diagnostics_state.active_tab.clone();
provide_context(diagnostics_state);
view! {
div(id="diagnostics") {
div(id="diagnostics-bar") {
RealizationStatus {}
select(bind:value=active_tab) {
option(value="loss") { "Loss" }
option(value="spectrum") { "Spectrum" }
}
}
DiagnosticsPanel(name="loss") { LossHistory {} }
DiagnosticsPanel(name="spectrum") { SpectrumHistory {} }
}
}
}

View file

@ -806,7 +806,6 @@ pub fn Display() -> View {
// again // again
canvas( canvas(
ref=display, ref=display,
id="display",
width="600", width="600",
height="600", height="600",
tabindex="0", tabindex="0",

View file

@ -256,18 +256,18 @@ pub struct DescentHistory {
pub config: Vec<DMatrix<f64>>, pub config: Vec<DMatrix<f64>>,
pub scaled_loss: Vec<f64>, pub scaled_loss: Vec<f64>,
pub neg_grad: Vec<DMatrix<f64>>, pub neg_grad: Vec<DMatrix<f64>>,
pub hess_eigvals: Vec::<DVector<f64>>, pub min_eigval: Vec<f64>,
pub base_step: Vec<DMatrix<f64>>, pub base_step: Vec<DMatrix<f64>>,
pub backoff_steps: Vec<i32> pub backoff_steps: Vec<i32>
} }
impl DescentHistory { impl DescentHistory {
pub fn new() -> DescentHistory { fn new() -> DescentHistory {
DescentHistory { DescentHistory {
config: Vec::<DMatrix<f64>>::new(), config: Vec::<DMatrix<f64>>::new(),
scaled_loss: Vec::<f64>::new(), scaled_loss: Vec::<f64>::new(),
neg_grad: Vec::<DMatrix<f64>>::new(), neg_grad: Vec::<DMatrix<f64>>::new(),
hess_eigvals: Vec::<DVector<f64>>::new(), min_eigval: Vec::<f64>::new(),
base_step: Vec::<DMatrix<f64>>::new(), base_step: Vec::<DMatrix<f64>>::new(),
backoff_steps: Vec::<i32>::new(), backoff_steps: Vec::<i32>::new(),
} }
@ -393,16 +393,6 @@ fn seek_better_config(
None 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 // seek a matrix `config` that matches the partial matrix `problem.frozen` and
// has `config' * Q * config` matching the partial matrix `problem.gram`. start // has `config' * Q * config` matching the partial matrix `problem.gram`. start
// at `problem.guess`, set the frozen entries to their desired values, and then // at `problem.guess`, set the frozen entries to their desired values, and then
@ -415,7 +405,7 @@ pub fn realize_gram(
reg_scale: f64, reg_scale: f64,
max_descent_steps: i32, max_descent_steps: i32,
max_backoff_steps: i32 max_backoff_steps: i32
) -> RealizationResult { ) -> (DMatrix<f64>, ConfigSubspace, bool, DescentHistory) {
// destructure the problem data // destructure the problem data
let ConstraintProblem { let ConstraintProblem {
gram, guess, frozen gram, guess, frozen
@ -467,12 +457,11 @@ pub fn realize_gram(
hess = DMatrix::from_columns(hess_cols.as_slice()); hess = DMatrix::from_columns(hess_cols.as_slice());
// regularize the Hessian // regularize the Hessian
let hess_eigvals = hess.symmetric_eigenvalues(); let min_eigval = hess.symmetric_eigenvalues().min();
let min_eigval = hess_eigvals.min();
if min_eigval <= 0.0 { if min_eigval <= 0.0 {
hess -= reg_scale * min_eigval * DMatrix::identity(total_dim, total_dim); hess -= reg_scale * min_eigval * DMatrix::identity(total_dim, total_dim);
} }
history.hess_eigvals.push(hess_eigvals); history.min_eigval.push(min_eigval);
// project the negative gradient and negative Hessian onto the // project the negative gradient and negative Hessian onto the
// orthogonal complement of the frozen subspace // orthogonal complement of the frozen subspace
@ -491,40 +480,30 @@ pub fn realize_gram(
if state.loss < tol { break; } if state.loss < tol { break; }
// compute the Newton step // compute the Newton step
/* TO DO */
/* /*
we should change our regularization to ensure that the Hessian is we need to either handle or eliminate the case where the minimum
is positive-definite, rather than just positive-semidefinite. ideally, eigenvalue of the Hessian is zero, so the regularized Hessian is
that would guarantee the success of the Cholesky decomposition--- singular. right now, this causes the Cholesky decomposition to return
although we'd still need the error-handling routine in case of `None`, leading to a panic when we unrap
numerical hiccups
*/ */
let hess_cholesky = match hess.clone().cholesky() { let base_step_stacked = hess.clone().cholesky().unwrap().solve(&neg_grad_stacked);
Some(cholesky) => cholesky,
None => return RealizationResult {
result: Err("Cholesky decomposition failed".to_string()),
history
}
};
let base_step_stacked = hess_cholesky.solve(&neg_grad_stacked);
let base_step = base_step_stacked.reshape_generic(Dyn(element_dim), Dyn(assembly_dim)); let base_step = base_step_stacked.reshape_generic(Dyn(element_dim), Dyn(assembly_dim));
history.base_step.push(base_step.clone()); history.base_step.push(base_step.clone());
// use backtracking line search to find a better configuration // use backtracking line search to find a better configuration
if let Some((better_state, backoff_steps)) = seek_better_config( match seek_better_config(
gram, &state, &base_step, neg_grad.dot(&base_step), gram, &state, &base_step, neg_grad.dot(&base_step),
min_efficiency, backoff, max_backoff_steps min_efficiency, backoff, max_backoff_steps
) { ) {
state = better_state; Some((better_state, backoff_steps)) => {
history.backoff_steps.push(backoff_steps); state = better_state;
} else { history.backoff_steps.push(backoff_steps);
return RealizationResult { },
result: Err("Line search failed".to_string()), None => return (state.config, ConfigSubspace::zero(assembly_dim), false, history)
history
}
}; };
} }
let result = if state.loss < tol { let success = state.loss < tol;
let tangent = if success {
// express the uniform basis in the standard basis // express the uniform basis in the standard basis
const UNIFORM_DIM: usize = 4; const UNIFORM_DIM: usize = 4;
let total_dim_unif = UNIFORM_DIM * assembly_dim; let total_dim_unif = UNIFORM_DIM * assembly_dim;
@ -537,13 +516,11 @@ pub fn realize_gram(
} }
// find the kernel of the Hessian. give it the uniform inner product // find the kernel of the Hessian. give it the uniform inner product
let tangent = ConfigSubspace::symmetric_kernel(hess, unif_to_std, assembly_dim); ConfigSubspace::symmetric_kernel(hess, unif_to_std, assembly_dim)
Ok(Realization { config: state.config, tangent })
} else { } else {
Err("Failed to reach target accuracy".to_string()) ConfigSubspace::zero(assembly_dim)
}; };
RealizationResult{ result, history } (state.config, tangent, success, history)
} }
// --- tests --- // --- tests ---
@ -562,7 +539,7 @@ pub mod examples {
// "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/
// //
pub fn realize_irisawa_hexlet(scaled_tol: f64) -> RealizationResult { pub fn realize_irisawa_hexlet(scaled_tol: f64) -> (DMatrix<f64>, ConfigSubspace, bool, DescentHistory) {
let mut problem = ConstraintProblem::from_guess( let mut problem = ConstraintProblem::from_guess(
[ [
sphere(0.0, 0.0, 0.0, 15.0), sphere(0.0, 0.0, 0.0, 15.0),
@ -613,7 +590,7 @@ pub mod examples {
// set up a kaleidocycle, made of points with fixed distances between them, // set up a kaleidocycle, made of points with fixed distances between them,
// and find its tangent space // and find its tangent space
pub fn realize_kaleidocycle(scaled_tol: f64) -> RealizationResult { pub fn realize_kaleidocycle(scaled_tol: f64) -> (DMatrix<f64>, ConfigSubspace, bool, DescentHistory) {
const N_HINGES: usize = 6; const N_HINGES: usize = 6;
let mut problem = ConstraintProblem::from_guess( let mut problem = ConstraintProblem::from_guess(
(0..N_HINGES).step_by(2).flat_map( (0..N_HINGES).step_by(2).flat_map(
@ -737,10 +714,10 @@ mod tests {
} }
problem.frozen.push(3, 0, problem.guess[(3, 0)]); problem.frozen.push(3, 0, problem.guess[(3, 0)]);
problem.frozen.push(3, 1, 0.5); problem.frozen.push(3, 1, 0.5);
let RealizationResult { result, history } = realize_gram( let (config, _, success, history) = realize_gram(
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110 &problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
); );
let config = result.unwrap().config; assert_eq!(success, true);
for base_step in history.base_step.into_iter() { for base_step in history.base_step.into_iter() {
for &MatrixEntry { index, .. } in &problem.frozen { for &MatrixEntry { index, .. } in &problem.frozen {
assert_eq!(base_step[index], 0.0); assert_eq!(base_step[index], 0.0);
@ -755,7 +732,7 @@ mod tests {
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 = realize_irisawa_hexlet(SCALED_TOL).result.unwrap().config; 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();
@ -782,11 +759,11 @@ mod tests {
for n in 0..ELEMENT_DIM { for n in 0..ELEMENT_DIM {
problem.frozen.push(n, 0, problem.guess[(n, 0)]); problem.frozen.push(n, 0, problem.guess[(n, 0)]);
} }
let RealizationResult { result, history } = realize_gram( let (config, tangent, success, history) = realize_gram(
&problem, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110 &problem, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110
); );
let Realization { config, tangent } = result.unwrap();
assert_eq!(config, problem.guess); assert_eq!(config, problem.guess);
assert_eq!(success, true);
assert_eq!(history.scaled_loss.len(), 1); assert_eq!(history.scaled_loss.len(), 1);
// list some motions that should form a basis for the tangent space of // list some motions that should form a basis for the tangent space of
@ -854,8 +831,8 @@ mod tests {
fn tangent_test_kaleidocycle() { fn tangent_test_kaleidocycle() {
// set up a kaleidocycle and find its tangent space // set up a kaleidocycle and find its tangent space
const SCALED_TOL: f64 = 1.0e-12; const SCALED_TOL: f64 = 1.0e-12;
let RealizationResult { result, history } = realize_kaleidocycle(SCALED_TOL); let (config, tangent, success, history) = realize_kaleidocycle(SCALED_TOL);
let Realization { config, tangent } = result.unwrap(); assert_eq!(success, true);
assert_eq!(history.scaled_loss.len(), 1); assert_eq!(history.scaled_loss.len(), 1);
// list some motions that should form a basis for the tangent space of // list some motions that should form a basis for the tangent space of
@ -943,11 +920,11 @@ mod tests {
problem_orig.gram.push_sym(0, 0, 1.0); problem_orig.gram.push_sym(0, 0, 1.0);
problem_orig.gram.push_sym(1, 1, 1.0); problem_orig.gram.push_sym(1, 1, 1.0);
problem_orig.gram.push_sym(0, 1, 0.5); problem_orig.gram.push_sym(0, 1, 0.5);
let RealizationResult { result: result_orig, history: history_orig } = realize_gram( let (config_orig, tangent_orig, success_orig, history_orig) = realize_gram(
&problem_orig, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110 &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!(config_orig, problem_orig.guess);
assert_eq!(success_orig, true);
assert_eq!(history_orig.scaled_loss.len(), 1); assert_eq!(history_orig.scaled_loss.len(), 1);
// find another pair of spheres that meet at 120°. we'll think of this // find another pair of spheres that meet at 120°. we'll think of this
@ -964,11 +941,11 @@ mod tests {
guess: guess_tfm, guess: guess_tfm,
frozen: problem_orig.frozen frozen: problem_orig.frozen
}; };
let RealizationResult { result: result_tfm, history: history_tfm } = realize_gram( let (config_tfm, tangent_tfm, success_tfm, history_tfm) = realize_gram(
&problem_tfm, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110 &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!(config_tfm, problem_tfm.guess);
assert_eq!(success_tfm, true);
assert_eq!(history_tfm.scaled_loss.len(), 1); assert_eq!(history_tfm.scaled_loss.len(), 1);
// project a nudge to the tangent space of the solution variety at the // project a nudge to the tangent space of the solution variety at the

View file

@ -1,6 +1,5 @@
mod add_remove; mod add_remove;
mod assembly; mod assembly;
mod diagnostics;
mod display; mod display;
mod engine; mod engine;
mod outline; mod outline;
@ -14,7 +13,6 @@ use sycamore::prelude::*;
use add_remove::AddRemove; use add_remove::AddRemove;
use assembly::{Assembly, Element}; use assembly::{Assembly, Element};
use diagnostics::Diagnostics;
use display::Display; use display::Display;
use outline::Outline; use outline::Outline;
@ -62,7 +60,6 @@ fn main() {
div(id="sidebar") { div(id="sidebar") {
AddRemove {} AddRemove {}
Outline {} Outline {}
Diagnostics {}
} }
Display {} Display {}
} }