feat: Engine diagnostics #92
565
app-proto/Cargo.lock
generated
|
@ -20,6 +20,21 @@ version = "0.2.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "approx"
|
||||
version = "0.5.1"
|
||||
|
@ -35,6 +50,21 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
|
@ -68,6 +98,35 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
|
@ -78,10 +137,122 @@ dependencies = [
|
|||
"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]]
|
||||
name = "dyna3"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"charming",
|
||||
"console_error_panic_hook",
|
||||
"dyna3",
|
||||
"itertools",
|
||||
|
@ -106,6 +277,22 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
|
@ -117,6 +304,28 @@ dependencies = [
|
|||
"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]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
@ -127,6 +336,12 @@ dependencies = [
|
|||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "html-escape"
|
||||
version = "0.2.13"
|
||||
|
@ -136,6 +351,47 @@ dependencies = [
|
|||
"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]]
|
||||
name = "indexmap"
|
||||
version = "2.5.0"
|
||||
|
@ -143,7 +399,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -155,6 +412,12 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.70"
|
||||
|
@ -192,6 +455,12 @@ dependencies = [
|
|||
"rawpointer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minicov"
|
||||
version = "0.3.5"
|
||||
|
@ -248,6 +517,12 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
|
@ -257,6 +532,21 @@ dependencies = [
|
|||
"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]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
|
@ -289,6 +579,57 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
|
@ -363,6 +704,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
version = "0.7.2"
|
||||
|
@ -387,6 +734,90 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
@ -421,14 +852,20 @@ version = "1.13.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "sycamore"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f38201dcb10aa609e81ca6f7547758a7eb602240a5ff682e668909fd0f7b2cc"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"indexmap",
|
||||
"hashbrown 0.14.5",
|
||||
"indexmap 2.5.0",
|
||||
"paste",
|
||||
"sycamore-core",
|
||||
"sycamore-macro",
|
||||
|
@ -444,7 +881,7 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41dc04bf0de321c6486356b2be751fac82fabb06c992d25b6748587561bba187"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
"paste",
|
||||
"sycamore-reactive",
|
||||
]
|
||||
|
@ -506,21 +943,78 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.77"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"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]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
|
@ -677,6 +1171,65 @@ dependencies = [
|
|||
"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]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
|
|
@ -17,6 +17,9 @@ nalgebra = "0.33.0"
|
|||
readonly = "0.2.12"
|
||||
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
|
||||
# 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
|
||||
|
|
36
app-proto/examples/common/print.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use nalgebra::DMatrix;
|
||||
|
||||
use dyna3::engine::{Q, DescentHistory, Realization};
|
||||
|
||||
pub fn title(title: &str) {
|
||||
println!("─── {title} ───");
|
||||
}
|
||||
|
||||
pub fn realization_diagnostics(realization: &Realization) {
|
||||
let Realization { result, history } = realization;
|
||||
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 gram_matrix(config: &DMatrix<f64>) {
|
||||
println!("\nCompleted Gram matrix:{}", (config.tr_mul(&*Q) * config).to_string().trim_end());
|
||||
}
|
||||
|
||||
pub fn config(config: &DMatrix<f64>) {
|
||||
println!("\nConfiguration:{}", config.to_string().trim_end());
|
||||
}
|
||||
|
||||
pub fn loss_history(history: &DescentHistory) {
|
||||
println!("\nStep │ Loss\n─────┼────────────────────────────────");
|
||||
for (step, scaled_loss) in history.scaled_loss.iter().enumerate() {
|
||||
println!("{:<4} │ {}", step, scaled_loss);
|
||||
}
|
||||
}
|
|
@ -1,25 +1,23 @@
|
|||
use dyna3::engine::{Q, examples::realize_irisawa_hexlet};
|
||||
#[path = "common/print.rs"]
|
||||
mod print;
|
||||
|
||||
use dyna3::engine::{ConfigNeighborhood, examples::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 {
|
||||
let realization = realize_irisawa_hexlet(SCALED_TOL);
|
||||
print::title("Irisawa hexlet");
|
||||
print::realization_diagnostics(&realization);
|
||||
if let Ok(ConfigNeighborhood { config, .. }) = realization.result {
|
||||
// print the diameters of the chain spheres
|
||||
println!("\nChain diameters:");
|
||||
println!(" {} sun (given)", 1.0 / config[(3, 3)]);
|
||||
for k in 4..9 {
|
||||
println!(" {} sun", 1.0 / config[(3, k)]);
|
||||
}
|
||||
|
||||
// print the completed Gram matrix
|
||||
print::gram_matrix(&config);
|
||||
}
|
||||
println!("\nStep │ Loss\n─────┼────────────────────────────────");
|
||||
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
|
||||
println!("{:<4} │ {}", step, scaled_loss);
|
||||
}
|
||||
print::loss_history(&realization.history);
|
||||
}
|
|
@ -1,30 +1,32 @@
|
|||
#[path = "common/print.rs"]
|
||||
mod print;
|
||||
|
||||
use nalgebra::{DMatrix, DVector};
|
||||
|
||||
use dyna3::engine::{Q, examples::realize_kaleidocycle};
|
||||
use dyna3::engine::{ConfigNeighborhood, examples::realize_kaleidocycle};
|
||||
|
||||
fn main() {
|
||||
const SCALED_TOL: f64 = 1.0e-12;
|
||||
let (config, tangent, success, history) = realize_kaleidocycle(SCALED_TOL);
|
||||
print!("Completed Gram matrix:{}", config.tr_mul(&*Q) * &config);
|
||||
print!("Configuration:{}", config);
|
||||
if success {
|
||||
println!("Target accuracy achieved!");
|
||||
} else {
|
||||
println!("Failed to reach target accuracy");
|
||||
let realization = realize_kaleidocycle(SCALED_TOL);
|
||||
print::title("Kaleidocycle");
|
||||
print::realization_diagnostics(&realization);
|
||||
if let Ok(ConfigNeighborhood { config, nbhd: tangent }) = realization.result {
|
||||
// print the completed Gram matrix and the realized configuration
|
||||
print::gram_matrix(&config);
|
||||
print::config(&config);
|
||||
|
||||
// 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);
|
||||
}
|
|
@ -1,4 +1,13 @@
|
|||
use dyna3::engine::{Q, point, realize_gram, sphere, ConstraintProblem};
|
||||
#[path = "common/print.rs"]
|
||||
mod print;
|
||||
|
||||
use dyna3::engine::{
|
||||
point,
|
||||
realize_gram,
|
||||
sphere,
|
||||
ConfigNeighborhood,
|
||||
ConstraintProblem
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut problem = ConstraintProblem::from_guess(&[
|
||||
|
@ -11,21 +20,14 @@ fn main() {
|
|||
}
|
||||
}
|
||||
problem.frozen.push(3, 0, problem.guess[(3, 0)]);
|
||||
println!();
|
||||
let (config, _, success, history) = realize_gram(
|
||||
let realization = realize_gram(
|
||||
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
print!("\nCompleted Gram matrix:{}", config.tr_mul(&*Q) * &config);
|
||||
print!("Configuration:{}", 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());
|
||||
println!("\nStep │ Loss\n─────┼────────────────────────────────");
|
||||
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
|
||||
println!("{:<4} │ {}", step, scaled_loss);
|
||||
print::title("Point on a sphere");
|
||||
print::realization_diagnostics(&realization);
|
||||
if let Ok(ConfigNeighborhood{ config, .. }) = realization.result {
|
||||
print::gram_matrix(&config);
|
||||
print::config(&config);
|
||||
}
|
||||
print::loss_history(&realization.history);
|
||||
}
|
|
@ -1,4 +1,12 @@
|
|||
use dyna3::engine::{Q, realize_gram, sphere, ConstraintProblem};
|
||||
#[path = "common/print.rs"]
|
||||
mod print;
|
||||
|
||||
use dyna3::engine::{
|
||||
realize_gram,
|
||||
sphere,
|
||||
ConfigNeighborhood,
|
||||
ConstraintProblem
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut problem = ConstraintProblem::from_guess({
|
||||
|
@ -14,20 +22,13 @@ fn main() {
|
|||
problem.gram.push_sym(j, k, if j == k { 1.0 } else { -1.0 });
|
||||
}
|
||||
}
|
||||
println!();
|
||||
let (config, _, success, history) = realize_gram(
|
||||
let realization = realize_gram(
|
||||
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
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());
|
||||
println!("\nStep │ Loss\n─────┼────────────────────────────────");
|
||||
for (step, scaled_loss) in history.scaled_loss.into_iter().enumerate() {
|
||||
println!("{:<4} │ {}", step, scaled_loss);
|
||||
print::title("Three spheres");
|
||||
print::realization_diagnostics(&realization);
|
||||
if let Ok(ConfigNeighborhood{ config, .. }) = realization.result {
|
||||
print::gram_matrix(&config);
|
||||
}
|
||||
print::loss_history(&realization.history);
|
||||
}
|
|
@ -6,6 +6,12 @@
|
|||
<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=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>
|
||||
<body></body>
|
||||
</html>
|
||||
|
|
|
@ -18,6 +18,17 @@ body {
|
|||
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 {
|
||||
|
@ -138,6 +149,7 @@ details[open]:has(li) .element-switch::after {
|
|||
}
|
||||
|
||||
.regulator-input {
|
||||
margin-right: 4px;
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
border: 1px solid var(--border);
|
||||
|
@ -159,22 +171,56 @@ details[open]:has(li) .element-switch::after {
|
|||
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 {
|
||||
content: '⚠';
|
||||
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 */
|
||||
|
||||
canvas {
|
||||
#display {
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
|
@ -183,7 +229,7 @@ canvas {
|
|||
border-radius: 16px;
|
||||
}
|
||||
|
||||
canvas:focus {
|
||||
#display:focus {
|
||||
border-color: var(--border-focus-dark);
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# run all Cargo examples, as described here:
|
||||
#
|
||||
# Karol Kuczmarski. "Add examples to your Rust libraries"
|
||||
# http://xion.io/post/code/rust-examples.html
|
||||
#
|
||||
|
||||
cargo run --example irisawa-hexlet
|
||||
cargo run --example three-spheres
|
||||
cargo run --example point-on-sphere
|
||||
cargo run --example kaleidocycle
|
20
app-proto/run-examples.sh
Normal file
|
@ -0,0 +1,20 @@
|
|||
# run all Cargo examples, as described here:
|
||||
#
|
||||
# Karol Kuczmarski. "Add examples to your Rust libraries"
|
||||
# http://xion.io/post/code/rust-examples.html
|
||||
#
|
||||
# you should invoke this script by calling `sh` or another interpreter, rather
|
||||
# than calling `souce`, to ensure that the script can find the manifest file for
|
||||
# the application prototype
|
||||
|
||||
# find the manifest file for the application prototype
|
||||
MANIFEST="$(dirname -- $0)/Cargo.toml"
|
||||
|
||||
# set up the command that runs each example
|
||||
RUN_EXAMPLE="cargo run --manifest-path $MANIFEST --example"
|
||||
|
||||
# run the examples
|
||||
$RUN_EXAMPLE irisawa-hexlet; echo
|
||||
$RUN_EXAMPLE three-spheres; echo
|
||||
$RUN_EXAMPLE point-on-sphere; echo
|
||||
$RUN_EXAMPLE kaleidocycle
|
|
@ -3,8 +3,9 @@ use sycamore::prelude::*;
|
|||
use web_sys::{console, wasm_bindgen::JsValue};
|
||||
|
||||
use crate::{
|
||||
engine,
|
||||
AppState,
|
||||
engine,
|
||||
engine::DescentHistory,
|
||||
assembly::{Assembly, InversiveDistanceRegulator, Point, Sphere}
|
||||
};
|
||||
|
||||
|
@ -195,6 +196,7 @@ pub fn AddRemove() -> View {
|
|||
assembly.regulators.update(|regs| regs.clear());
|
||||
assembly.elements.update(|elts| elts.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());
|
||||
|
||||
// load assembly
|
||||
|
|
|
@ -23,8 +23,11 @@ use crate::{
|
|||
project_sphere_to_normalized,
|
||||
realize_gram,
|
||||
sphere,
|
||||
ConfigNeighborhood,
|
||||
ConfigSubspace,
|
||||
ConstraintProblem
|
||||
ConstraintProblem,
|
||||
DescentHistory,
|
||||
Realization
|
||||
},
|
||||
outline::OutlineItem,
|
||||
specified::SpecifiedValue
|
||||
|
@ -547,7 +550,11 @@ pub struct Assembly {
|
|||
pub tangent: Signal<ConfigSubspace>,
|
||||
|
||||
// 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 {
|
||||
|
@ -556,7 +563,9 @@ impl Assembly {
|
|||
elements: create_signal(BTreeSet::new()),
|
||||
regulators: create_signal(BTreeSet::new()),
|
||||
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())
|
||||
glen marked this conversation as resolved
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -687,31 +696,49 @@ impl Assembly {
|
|||
console_log!("Old configuration:{:>8.3}", problem.guess);
|
||||
|
||||
// look for a configuration with the given Gram matrix
|
||||
let (config, tangent, success, history) = realize_gram(
|
||||
let Realization { result, history } = realize_gram(
|
||||
&problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
|
||||
/* DEBUG */
|
||||
// report the outcome of the search
|
||||
if success {
|
||||
console_log!("Target accuracy achieved!")
|
||||
// report the outcome of the search in the browser console
|
||||
if let Err(ref message) = result {
|
||||
console_log!("❌️ {message}");
|
||||
} else {
|
||||
console_log!("Failed to reach target accuracy")
|
||||
console_log!("✅️ Target accuracy achieved!");
|
||||
}
|
||||
console_log!("Steps: {}", history.scaled_loss.len() - 1);
|
||||
console_log!("Loss: {}", *history.scaled_loss.last().unwrap());
|
||||
console_log!("Tangent dimension: {}", tangent.dim());
|
||||
console_log!("Loss: {}", history.scaled_loss.last().unwrap());
|
||||
|
||||
if success {
|
||||
// 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()))
|
||||
);
|
||||
// report the loss history
|
||||
self.descent_history.set(history);
|
||||
|
||||
match result {
|
||||
Ok(ConfigNeighborhood { config, nbhd: tangent }) => {
|
||||
glen
commented
Is Is `nbhd: tangent` just a syntax for in essence renaming the `nbhd` member of a ConfigNeighborhood to `tangent` for use as a local variable? Is the fact that you are inclined to do so an indication that perhaps `config` and `tangent` would be better names for the members of a ConfigNeighborhood rather than `config` and `nbhd`? In other words, you seem comfortable enough with config just to use it here, but felt motivated to use the `nbhd` under the name `tangent`...
Vectornaut
commented
Yes—this code destructures
That field was indeed called I'd be open to switching to a name like
However, I haven't been able to find one. > Is nbhd: tangent just a syntax for in essence renaming the nbhd member of a ConfigNeighborhood to tangent for use as a local variable?
Yes—this code [*destructures*](https://doc.rust-lang.org/rust-by-example/flow_control/match/destructuring/destructure_structures.html) `result` into local variables called `config` and `tangent`, which hold the values of `result.config` and `result.nbhd` respectively.
> Is the fact that you are inclined to do so an indication that perhaps `config` and `tangent` would be better names for the members of a ConfigNeighborhood rather than `config` and `nbhd`?
That field was indeed called `tangent` before the [naming improvement commit](https://code.studioinfinity.org/StudioInfinity/dyna3/commit/f1865f8#diff-1357a04457079bf2e3ec43e8436b5f77f70471c1). I renamed it to `nbhd` because the `ConfigNeighborhood` structure seems well-designed to represent any first-order neighborhood of a point in configuration space, regardless of whether we're thinking of it as the tangent space of a submanifold. To me, this aligns with our renaming of the `ConfigNeighborhood` structure itself, which recognized that this structure is well-designed to represent things other than the result of a realization.
I'd be open to switching to a name like `ConfigTangent` or `ConfigTangentPlane` for the structure and going back to `tangent` for the field in that context. I still feel like there must be a better name for the structure, like maybe:
- A name for what you get when you take a [distribution](https://en.wikipedia.org/wiki/Distribution_(differential_geometry)) on a vector bundle and evaluate it at a point.
- Something related to the notion of a [first-order neighborhood](https://mathoverflow.net/questions/78313/first-order-infinitesimal-neighborhood-of-a-point) used in algebraic geometry.
However, I haven't been able to find one.
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
258
app-proto/src/diagnostics.rs
Normal file
|
@ -0,0 +1,258 @@
|
|||
use charming::{
|
||||
Chart,
|
||||
WasmRenderer,
|
||||
component::{Axis, DataZoom, Grid},
|
||||
element::{AxisType, Symbol},
|
||||
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()
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn into_log10_time_point((step, value): (usize, f64)) -> Vec<Option<f64>> {
|
||||
vec![
|
||||
Some(step as f64),
|
||||
if value == 0.0 { None } else { Some(value.abs().log10()) }
|
||||
]
|
||||
}
|
||||
|
||||
// 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: Vec<_> = state.assembly.descent_history.with(
|
||||
|history| history.scaled_loss
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(step, &loss)| (step, loss))
|
||||
.map(into_log10_time_point)
|
||||
.collect()
|
||||
);
|
||||
|
||||
// initialize the chart axes
|
||||
let step_axis = Axis::new()
|
||||
.type_(AxisType::Category)
|
||||
.boundary_gap(false);
|
||||
let scaled_loss_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 scaled_loss_series = Line::new().data(
|
||||
if scaled_loss.len() > 0 {
|
||||
scaled_loss
|
||||
} else {
|
||||
vec![vec![Some(0.0), None::<f64>]]
|
||||
}
|
||||
);
|
||||
let chart = Chart::new()
|
||||
.animation(false)
|
||||
.data_zoom(DataZoom::new().y_axis_index(0).right(40))
|
||||
.x_axis(step_axis)
|
||||
.y_axis(scaled_loss_axis)
|
||||
.grid(Grid::new().top(20).right(80).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, split into its
|
||||
// positive, negative, and strictly-zero parts
|
||||
let (
|
||||
hess_eigvals_zero,
|
||||
hess_eigvals_nonzero
|
||||
): (Vec<_>, Vec<_>) = state.assembly.descent_history.with(
|
||||
|history| history.hess_eigvals
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(
|
||||
|(step, eigvals)| eigvals.iter().map(
|
||||
move |&val| (step, val)
|
||||
)
|
||||
)
|
||||
.flatten()
|
||||
.partition(|&(_, val)| val == 0.0)
|
||||
);
|
||||
let zero_level = hess_eigvals_nonzero
|
||||
.iter()
|
||||
.map(|(_, val)| val.abs())
|
||||
.reduce(f64::min)
|
||||
.map(|val| 0.1 * val)
|
||||
.unwrap_or(1.0);
|
||||
let (
|
||||
hess_eigvals_pos,
|
||||
hess_eigvals_neg
|
||||
): (Vec<_>, Vec<_>) = hess_eigvals_nonzero
|
||||
.into_iter()
|
||||
.partition(|&(_, val)| val > 0.0);
|
||||
|
||||
// initialize the chart axes
|
||||
let step_axis = Axis::new()
|
||||
.type_(AxisType::Category)
|
||||
.boundary_gap(false);
|
||||
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 eigval_series_pos = Scatter::new()
|
||||
.symbol_size(4.5)
|
||||
.data(
|
||||
if hess_eigvals_pos.len() > 0 {
|
||||
hess_eigvals_pos
|
||||
.into_iter()
|
||||
.map(into_log10_time_point)
|
||||
.collect()
|
||||
} else {
|
||||
vec![vec![Some(0.0), None::<f64>]]
|
||||
}
|
||||
);
|
||||
let eigval_series_neg = Scatter::new()
|
||||
.symbol(Symbol::Diamond)
|
||||
.symbol_size(6.0)
|
||||
.data(
|
||||
if hess_eigvals_neg.len() > 0 {
|
||||
hess_eigvals_neg
|
||||
.into_iter()
|
||||
.map(into_log10_time_point)
|
||||
.collect()
|
||||
} else {
|
||||
vec![vec![Some(0.0), None::<f64>]]
|
||||
}
|
||||
);
|
||||
let eigval_series_zero = Scatter::new()
|
||||
.symbol(Symbol::Triangle)
|
||||
.symbol_size(5.0)
|
||||
.data(
|
||||
if hess_eigvals_zero.len() > 0 {
|
||||
hess_eigvals_zero
|
||||
.into_iter()
|
||||
.map(|(step, _)| (step, zero_level))
|
||||
.map(into_log10_time_point)
|
||||
.collect()
|
||||
} else {
|
||||
vec![vec![Some(0.0), None::<f64>]]
|
||||
}
|
||||
);
|
||||
let chart = Chart::new()
|
||||
.animation(false)
|
||||
.data_zoom(DataZoom::new().y_axis_index(0).right(40))
|
||||
.x_axis(step_axis)
|
||||
.y_axis(eigval_axis)
|
||||
.grid(Grid::new().top(20).right(80).bottom(30).left(60))
|
||||
.series(eigval_series_pos)
|
||||
.series(eigval_series_neg)
|
||||
.series(eigval_series_zero);
|
||||
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 {} }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -806,6 +806,7 @@ pub fn Display() -> View {
|
|||
// again
|
||||
canvas(
|
||||
ref=display,
|
||||
id="display",
|
||||
width="600",
|
||||
height="600",
|
||||
tabindex="0",
|
||||
|
|
|
@ -256,18 +256,18 @@ pub struct DescentHistory {
|
|||
pub config: Vec<DMatrix<f64>>,
|
||||
pub scaled_loss: Vec<f64>,
|
||||
pub neg_grad: Vec<DMatrix<f64>>,
|
||||
pub min_eigval: Vec<f64>,
|
||||
pub hess_eigvals: Vec::<DVector<f64>>,
|
||||
pub base_step: Vec<DMatrix<f64>>,
|
||||
pub backoff_steps: Vec<i32>
|
||||
}
|
||||
|
||||
impl DescentHistory {
|
||||
fn new() -> DescentHistory {
|
||||
pub fn new() -> DescentHistory {
|
||||
DescentHistory {
|
||||
config: Vec::<DMatrix<f64>>::new(),
|
||||
scaled_loss: Vec::<f64>::new(),
|
||||
neg_grad: Vec::<DMatrix<f64>>::new(),
|
||||
min_eigval: Vec::<f64>::new(),
|
||||
hess_eigvals: Vec::<DVector<f64>>::new(),
|
||||
base_step: Vec::<DMatrix<f64>>::new(),
|
||||
backoff_steps: Vec::<i32>::new(),
|
||||
}
|
||||
|
@ -393,6 +393,17 @@ fn seek_better_config(
|
|||
None
|
||||
}
|
||||
|
||||
// a first-order neighborhood of a configuration
|
||||
pub struct ConfigNeighborhood {
|
||||
pub config: DMatrix<f64>,
|
||||
pub nbhd: ConfigSubspace
|
||||
}
|
||||
|
||||
glen marked this conversation as resolved
glen
commented
The difference between a "Realization" and a "RealizationResult" is not at all clear to me. I guess that this is some Rust-ism, in that a Result is some customary wrapper around a value, in this case a Realization -- is that right? Do you think the two names are completely idiomatic in a Rust world? Or do you think there might be some plain-english names that would better convey what's going on? (Intuitively to me, it would seem a Realization would be the natural outcome of trying to realize an Assembly...) The difference between a "Realization" and a "RealizationResult" is not at all clear to me. I guess that this is some Rust-ism, in that a Result is some customary wrapper around a value, in this case a Realization -- is that right? Do you think the two names are completely idiomatic in a Rust world? Or do you think there might be some plain-english names that would better convey what's going on? (Intuitively to me, it would seem a Realization would be the natural outcome of trying to realize an Assembly...)
Vectornaut
commented
Trying to realize an assembly doesn't always produce a realization (because it can fail), and it also produces additional data (the descent history and other diagnostic information). Are you hoping for a better way to organize this data, or just a more descriptive name for the data structure? If the latter, what do you think of
No, I don't think the current naming is idiomatic. In fact, I find it a little misleading, because I'd expect something called > Intuitively to me, it would seem a Realization would be the natural outcome of trying to realize an Assembly...
Trying to realize an assembly doesn't always produce a realization (because it can fail), and it also produces additional data (the descent history and other diagnostic information). Are you hoping for a better way to organize this data, or just a more descriptive name for the data structure? If the latter, what do you think of `RealizationOutcome` or `RealizationOutput`?
> I guess that this is some Rust-ism, in that a Result is some customary wrapper around a value, in this case a Realization -- is that right? Do you think the two names are completely idiomatic in a Rust world?
No, I don't think the current naming is idiomatic. In fact, I find it a little misleading, because I'd expect something called `RealizationResult` to just be or contain a `Result<Realization, _>`, with no additional data.
glen
commented
OK, so currently a Realization is just a set of coordinates for everything, is that right? The other gadget is all the stuff that can be produced from a call to a "realize" function that tries to create a Realization that satisfies all the constraints, but (a) might fail, and (b) produces additional info, correct? So yes, I guess all I am looking for are better names for these two things. Here are some options: (a) What is currently called a Realization should perhaps just be called a Configuration -- if it is just coordinates, there's no guarantee that this data structure "realizes" any particular set of constraints. That would free up "Realization" to be, quite naturally, the result of calling (b) If you feel strongly that the collection of coordinates thing should absolutely be called a Realization (although to me that only makes sense if there is something inherent in the data structure that ensures its contents realize some specific constraings), then the gadget returned by having the engine try to realize() could be named something altogether different, like "EngineRun" or "EngineData." I guess what I am mostly saying is to choose quite distinct stem words for the two structures, to reflect the fact that this is a containment relationship, rather than an is-a or flavor-of relationship: an output-of-realize gadget optionally contains one of the collection-of-coordinates things, but it is an altogether different entity. Hopefully I am making sense. OK, so currently a Realization is just a set of coordinates for everything, is that right? The other gadget is all the stuff that can be produced from a call to a "realize" function that tries to create a Realization that satisfies all the constraints, but (a) might fail, and (b) produces additional info, correct?
So yes, I guess all I am looking for are better names for these two things. Here are some options:
(a) What is currently called a Realization should perhaps just be called a Configuration -- if it is just coordinates, there's no guarantee that this data structure "realizes" any particular set of constraints. That would free up "Realization" to be, quite naturally, the result of calling `realize()` -- and such a thing would naturally include a Configuration if it managed to find one. (But again, I don't quite understand why it wouldn't _always_ include a Configuration (the best it could do) and a flag to tell you whether this configuration actually realized all the constraints.)
(b) If you feel strongly that the collection of coordinates thing should absolutely be called a Realization (although to me that only makes sense if there is something inherent in the data structure that ensures its contents realize some specific constraings), then the gadget returned by having the engine try to realize() could be named something altogether different, like "EngineRun" or "EngineData."
I guess what I am mostly saying is to choose quite distinct stem words for the two structures, to reflect the fact that this is a containment relationship, rather than an is-a or flavor-of relationship: an output-of-realize gadget optionally contains one of the collection-of-coordinates things, but it is an altogether different entity.
Hopefully I am making sense.
Vectornaut
commented
That makes the most sense to me! The structures would become something like this:
> What is currently called a Realization should perhaps just be called a Configuration -- if it is just coordinates, there's no guarantee that this data structure "realizes" any particular set of constraints. That would free up "Realization" to be, quite naturally, the result of calling `realize()` -- and such a thing would naturally include a Configuration if it managed to find one.
That makes the most sense to me! The structures would become something like this:
```rust
pub struct Configuration {
pub config: DMatrix<f64>, /* rename? */
pub tangent: ConfigSubspace
}
pub struct Realization {
pub config: Result<Configuration, String>,
pub history: DescentHistory
}
```
Vectornaut
commented
During our meeting, we decided to name the structures like this:
In the future, we might want the engine to return a best guess even if realization fails. Ways to do that include:
During our meeting, we decided to name the structures like this:
```rust
pub type Configuration = DMatrix<f64>;
// a first-order neighborhood of a configuration
pub struct ConfigNeighborhood {
pub config: Configuration
pub neighborhood: ConfigSubspace
}
pub struct Realization {
pub config_neighborhood: Result<ConfigNeighborhood, String>,
pub history: DescentHistory
}
```
In the future, we might want the engine to return a best guess even if realization fails. Ways to do that include:
- Splitting `config` into a `ConfigNeighborhood` and a success indicator.
- Changing the type of `config` to something like this:
```rust
Result<ConfigNeighborhood, (ConfigNeighborhood, String)>
```
The `ConfigNeighborhood` in the `Err` variant would be the best guess.
glen
commented
Two thoughts:
Two thoughts:
* In the new Realization structure, the ConfigNeighborhood member could be called "outcome" or "output" or something -- it's name needn't just reiterate its type.
* In the possible "best guess" future world, we may still want to allow the engine sometimes to throw up its hands completely. So that would mean the two ways you suggest would become having the outcome be an `Option<ConfigNeighboorhood>` together with a success indicator, or having it be something like `Result<ConfigNeighboorhood, (Option<ConfigNeighborhood>, String)`. But either way (optional best guess or mandatory best guess), having a ConfigNeighborhood on both sides of the Result _feels_ a bit redundant.
Vectornaut
commented
How about keeping the name > In the new Realization structure, the ConfigNeighborhood member could be called "outcome" or "output" or something -- it's name needn't just reiterate its type.
How about keeping the name `result`? This both hints that it has a `Result` type and accurately describes that it's the main result of trying to realize (in contrast to the extra data in `history`). To me, it feels similar to `outcome`, but a little better because it provides the type hint.
Vectornaut
commented
glen
commented
These names seem fine, so closing, but note I did raise one question about the name of a member of ConfigNeighborhood. These names seem fine, so closing, but note I did raise one question about the name of a member of ConfigNeighborhood.
|
||||
pub struct Realization {
|
||||
pub result: Result<ConfigNeighborhood, 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 +416,7 @@ pub fn realize_gram(
|
|||
reg_scale: f64,
|
||||
max_descent_steps: i32,
|
||||
max_backoff_steps: i32
|
||||
) -> (DMatrix<f64>, ConfigSubspace, bool, DescentHistory) {
|
||||
) -> Realization {
|
||||
// destructure the problem data
|
||||
let ConstraintProblem {
|
||||
gram, guess, frozen
|
||||
|
@ -457,11 +468,12 @@ pub fn realize_gram(
|
|||
hess = DMatrix::from_columns(hess_cols.as_slice());
|
||||
|
||||
// regularize the Hessian
|
||||
let min_eigval = hess.symmetric_eigenvalues().min();
|
||||
let hess_eigvals = hess.symmetric_eigenvalues();
|
||||
let min_eigval = hess_eigvals.min();
|
||||
if min_eigval <= 0.0 {
|
||||
hess -= reg_scale * min_eigval * DMatrix::identity(total_dim, total_dim);
|
||||
}
|
||||
history.min_eigval.push(min_eigval);
|
||||
history.hess_eigvals.push(hess_eigvals);
|
||||
|
||||
// project the negative gradient and negative Hessian onto the
|
||||
// orthogonal complement of the frozen subspace
|
||||
|
@ -480,30 +492,40 @@ pub fn realize_gram(
|
|||
if state.loss < tol { break; }
|
||||
|
||||
// compute the Newton step
|
||||
/* TO DO */
|
||||
/*
|
||||
we need to either handle or eliminate the case where the minimum
|
||||
eigenvalue of the Hessian is zero, so the regularized Hessian is
|
||||
singular. right now, this causes the Cholesky decomposition to return
|
||||
`None`, leading to a panic when we unrap
|
||||
we should change our regularization to ensure that the Hessian is
|
||||
is positive-definite, rather than just positive-semidefinite. ideally,
|
||||
that would guarantee the success of the Cholesky decomposition---
|
||||
although we'd still need the error-handling routine in case of
|
||||
numerical hiccups
|
||||
*/
|
||||
let base_step_stacked = hess.clone().cholesky().unwrap().solve(&neg_grad_stacked);
|
||||
let hess_cholesky = match hess.clone().cholesky() {
|
||||
Some(cholesky) => cholesky,
|
||||
None => return Realization {
|
||||
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));
|
||||
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 Realization {
|
||||
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 +538,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(ConfigNeighborhood { config: state.config, nbhd: tangent })
|
||||
} else {
|
||||
ConfigSubspace::zero(assembly_dim)
|
||||
Err("Failed to reach target accuracy".to_string())
|
||||
};
|
||||
(state.config, tangent, success, history)
|
||||
Realization { result, history }
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
@ -539,7 +563,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) -> Realization {
|
||||
let mut problem = ConstraintProblem::from_guess(
|
||||
[
|
||||
sphere(0.0, 0.0, 0.0, 15.0),
|
||||
|
@ -590,7 +614,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) -> Realization {
|
||||
const N_HINGES: usize = 6;
|
||||
let mut problem = ConstraintProblem::from_guess(
|
||||
(0..N_HINGES).step_by(2).flat_map(
|
||||
|
@ -714,10 +738,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 Realization { 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 +756,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 +783,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 Realization { result, history } = realize_gram(
|
||||
&problem, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
let ConfigNeighborhood { config, nbhd: 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 +855,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 Realization { result, history } = realize_kaleidocycle(SCALED_TOL);
|
||||
let ConfigNeighborhood { config, nbhd: 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 +944,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 Realization { result: result_orig, history: history_orig } = realize_gram(
|
||||
&problem_orig, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
let ConfigNeighborhood { config: config_orig, nbhd: 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 +965,11 @@ mod tests {
|
|||
guess: guess_tfm,
|
||||
frozen: problem_orig.frozen
|
||||
};
|
||||
let (config_tfm, tangent_tfm, success_tfm, history_tfm) = realize_gram(
|
||||
let Realization { result: result_tfm, history: history_tfm } = realize_gram(
|
||||
&problem_tfm, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110
|
||||
);
|
||||
let ConfigNeighborhood { config: config_tfm, nbhd: 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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod add_remove;
|
||||
mod assembly;
|
||||
mod diagnostics;
|
||||
mod display;
|
||||
mod engine;
|
||||
mod outline;
|
||||
|
@ -13,6 +14,7 @@ use sycamore::prelude::*;
|
|||
|
||||
use add_remove::AddRemove;
|
||||
use assembly::{Assembly, Element};
|
||||
use diagnostics::Diagnostics;
|
||||
use display::Display;
|
||||
use outline::Outline;
|
||||
|
||||
|
@ -60,6 +62,7 @@ fn main() {
|
|||
div(id="sidebar") {
|
||||
AddRemove {}
|
||||
Outline {}
|
||||
Diagnostics {}
|
||||
}
|
||||
Display {}
|
||||
}
|
||||
|
|
Conceptually, does the descent history belong as a member of an Assembly?
My perspective here is that realization happens within an assembly, so its results—including the descent history—belong to the assembly.
The upcoming history display pull feature will force us to rethink that, because we decided to implement that feature by turning the realization history into a whole stack of assemblies—one for each descent step.
I propose keeping the current organization for this pull request, because it's consistent with the way I've been thinking about realization, and then reorganizing while we write the history display feature.
Yes, this is fine -- I think something closer to my perspective is that an assembly is a geometric arrangement together with a list of properties it is intended to have (i.e., constraints). I think that will be consistent with the reorganization you propose.